ServerSession.java revision 2e0da96e757a977154063f980d3f4e1abd41cf09
19439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly/* 29439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Copyright (c) 2008-2009, Motorola, Inc. 39439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 49439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * All rights reserved. 59439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 69439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Redistribution and use in source and binary forms, with or without 79439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * modification, are permitted provided that the following conditions are met: 89439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 99439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Redistributions of source code must retain the above copyright notice, 109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * this list of conditions and the following disclaimer. 119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Redistributions in binary form must reproduce the above copyright notice, 139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * this list of conditions and the following disclaimer in the documentation 149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * and/or other materials provided with the distribution. 159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Neither the name of the Motorola, Inc. nor the names of its contributors 179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * may be used to endorse or promote products derived from this software 189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * without specific prior written permission. 199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * POSSIBILITY OF SUCH DAMAGE. 319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellypackage javax.obex; 349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 352e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.io.InputStream; 362e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.io.IOException; 372e0da96e757a977154063f980d3f4e1abd41cf09Nick Pellyimport java.io.OutputStream; 389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly/** 409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * This class in an implementation of the ServerSession interface. 419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 422e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @hide 439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellypublic class ServerSession implements Runnable, ObexSession { 459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private ObexTransport client; 479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private InputStream input; 499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private OutputStream output; 519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private ServerRequestHandler listener; 539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private Thread processThread; 559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private int maxPacketLength; 579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private Authenticator authenticator; 599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] challengeDigest; 619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 622e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly private boolean isClosed; 639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Creates new ServerSession. 669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param conn 689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the connection to the client 699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param handler 719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the event listener that will process requests 729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param auth 749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the authenticator to use with this connection 759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 762e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @throws IOException 779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if an error occurred while opening the input and output 789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * streams 799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public ServerSession(ObexTransport conn, ServerRequestHandler handler, Authenticator auth) 819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly throws IOException { 829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly authenticator = auth; 839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly client = conn; 849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly input = client.openInputStream(); 859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output = client.openOutputStream(); 869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly listener = handler; 879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly maxPacketLength = 256; 889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly isClosed = false; 909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly processThread = new Thread(this); 919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly processThread.start(); 929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* removed as they're provided to the API user. Not used internally. */ 959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public boolean isCreatedServer() { 979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (client instanceof BTConnection) 989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return ((BTConnection)client).isServerCreated(); 999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly else 1009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 1019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 1029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public boolean isClosed() { 1049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (client instanceof BTConnection) 1059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return ((BTConnection)client).isClosed(); 1069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly else 1079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 1089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 1099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public int getConnectionHandle() { 1119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (client instanceof BTConnection) 1129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return ((BTConnection)client).getConnectionHandle(); 1139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly else 1149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return -1; 1159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 1169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public RemoteDevice getRemoteDevice() { 1189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (client instanceof BTConnection) 1199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return ((BTConnection)client).getRemoteDevice(); 1209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly else 1219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return null; 1229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly }*/ 1239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 1259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Processes requests made to the server and forwards them to the 1269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * appropriate event listener. 1279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 1289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public void run() { 1299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 1309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly boolean done = false; 1329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly while (!done && !isClosed) { 1339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int requestType = input.read(); 1349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly switch (requestType) { 1359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x80: 1369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly handleConnectRequest(); 1379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly break; 1389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x81: 1409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly handleDisconnectRequest(); 1419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly done = true; 1429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly break; 1439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x03: 1459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x83: 1469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly handleGetRequest(requestType); 1479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly break; 1489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x02: 1509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x82: 1519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly handlePutRequest(requestType); 1529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly break; 1539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x85: 1559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly handleSetPathRequest(); 1569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly break; 1579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case -1: 1599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly done = true; 1609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly break; 1619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly default: 1639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 1659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Received a request type that is not recognized so I am 1669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * just going to read the packet and send a not implemented 1679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * to the client 1689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 1699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int length = input.read(); 1709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly length = (length << 8) + input.read(); 1719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly for (int i = 3; i < length; i++) { 1729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly input.read(); 1739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 1749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null); 1759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // done = true; 1779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 1789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 1799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (NullPointerException e) { 1819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 1829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 1839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly close(); 1849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 1859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 1879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Handles a PUT request from a client. This method will provide a 1889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerOperation</code> object to the request handler. The 1899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerOperation</code> object will handle the rest of the request. 1909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * It will also send replies and receive requests until the final reply 1919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * should be sent. When the final reply should be sent, this method will get 1929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the response code to use and send the reply. The 1939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerOperation</code> object will always reply with a 1949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * OBEX_HTTP_CONTINUE reply. It will only reply if further information is 1959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * needed. 1969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 1979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param type 1989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the type of request received; either 0x02 or 0x82 1999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 2002e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @throws IOException 2019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if an error occurred at the transport layer 2029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 2039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private void handlePutRequest(int type) throws IOException { 2049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener); 2059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 2069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int response = -1; 2079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 2089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((client.finalBitSet) && !client.isValidBody()) { 2099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly response = validateResponseCode(listener.onDelete(client.requestHeaders, 2109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly client.replyHeaders)); 2119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 2129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly response = validateResponseCode(listener.onPut(client)); 2139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (response != ResponseCodes.OBEX_HTTP_OK) { 2159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly client.sendReply(response); 2169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else if (!client.isAborted) { 2179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // wait for the final bit 2189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly while (!client.finalBitSet) { 2192e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly client.sendReply(ObexHelper.OBEX_HTTP_CONTINUE); 2209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly client.sendReply(response); 2229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 2249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 2259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 2289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 2299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Handles a GET request from a client. This method will provide a 2309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerOperation</code> object to the request handler. The 2319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerOperation</code> object will handle the rest of the request. 2329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * It will also send replies and receive requests until the final reply 2339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * should be sent. When the final reply should be sent, this method will get 2349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the response code to use and send the reply. The 2359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerOperation</code> object will always reply with a 2369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * OBEX_HTTP_CONTINUE reply. It will only reply if further information is 2379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * needed. 2389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 2399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param type 2409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the type of request received; either 0x03 or 0x83 2419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 2422e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @throws IOException 2439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if an error occurred at the transport layer 2449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 2459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private void handleGetRequest(int type) throws IOException { 2469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener); 2479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 2489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int response = validateResponseCode(listener.onGet(client)); 2499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 2509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (!client.isAborted) { 2519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly client.sendReply(response); 2529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 2549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 2559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 2589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 2599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Send standard response. 2609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 2619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param code 2629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the response code to send 2639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 2649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param header 2659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the headers to include in the response 2669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 2672e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @throws IOException 2689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if an IO error occurs 2699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 2709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly protected void sendResponse(int code, byte[] header) throws IOException { 2719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int totalLength = 3; 2729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] data = null; 2739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 2749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (header != null) { 2759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength += header.length; 2769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly data = new byte[totalLength]; 2779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly data[0] = (byte)code; 2789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly data[1] = (byte)(totalLength >> 8); 2799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly data[2] = (byte)totalLength; 2809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(header, 0, data, 3, header.length); 2819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 2829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly data = new byte[totalLength]; 2839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly data[0] = (byte)code; 2849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly data[1] = (byte)0x00; 2859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly data[2] = (byte)totalLength; 2869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.write(data); 2889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.flush(); 2899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 2909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 2919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 2929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Handles a SETPATH request from a client. This method will read the rest 2939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * of the request from the client. Assuming the request is valid, it will 2949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * create a <code>HeaderSet</code> object to pass to the 2959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerRequestHandler</code> object. After the handler processes the 2969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * request, this method will create a reply message to send to the server 2979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * with the response code provided. 2989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 2992e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @throws IOException 3009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if an error occurred at the transport layer 3019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 3029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private void handleSetPathRequest() throws IOException { 3039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int length; 3049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int flags; 3059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int constants; 3069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int totalLength = 3; 3079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] head = null; 3089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int code = -1; 3099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int bytesReceived; 3109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly HeaderSet request = new HeaderSet(); 3119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly HeaderSet reply = new HeaderSet(); 3129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly length = input.read(); 3149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly length = (length << 8) + input.read(); 3159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly flags = input.read(); 3169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly constants = input.read(); 3179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3182e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly if (length > ObexHelper.MAX_PACKET_SIZE_INT) { 3199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE; 3209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength = 3; 3219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 3229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (length > 5) { 3239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] headers = new byte[length - 5]; 3249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly bytesReceived = input.read(headers); 3259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly while (bytesReceived != headers.length) { 3279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly bytesReceived += input.read(headers, bytesReceived, headers.length 3289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly - bytesReceived); 3299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3312e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly ObexHelper.updateHeaderSet(request, headers); 3329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.connectionID != null) { 3342e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly listener.setConnectionID(ObexHelper.convertToLong(request.connectionID)); 3359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 3369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly listener.setConnectionID(-1); 3379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // the Auth chan is initiated by the server. 3399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // client sent back the authResp . 3409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.authResp != null) { 3419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (!handleAuthResp(request.authResp)) { 3429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED; 3432e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly listener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01, 3449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authResp)); 3459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authResp = null; 3479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) { 3519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // the Auth chan is initiated by the client 3529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // the server will send back the authResp to the client 3539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.authChall != null) { 3549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly handleAuthChall(request); 3559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly reply.authResp = new byte[request.authResp.length]; 3569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length); 3579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authChall = null; 3589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authResp = null; 3599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly boolean backup = false; 3619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly boolean create = true; 3629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (!((flags & 1) == 0)) { 3639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly backup = true; 3649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((flags & 2) == 0) { 3669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly create = false; 3679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 3709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = listener.onSetPath(request, reply, backup, create); 3719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 3729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 3739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return; 3749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = validateResponseCode(code); 3779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (reply.nonce != null) { 3799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly challengeDigest = new byte[16]; 3809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); 3819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 3829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly challengeDigest = null; 3839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly long id = listener.getConnectionID(); 3869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (id == -1) { 3879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly reply.connectionID = null; 3889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 3892e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly reply.connectionID = ObexHelper.convertToByteArray(id); 3909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 3919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3922e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly head = ObexHelper.createHeader(reply, false); 3939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength += head.length; 3949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 3959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (totalLength > maxPacketLength) { 3969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength = 3; 3979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly head = null; 3989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 3999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Compute Length of OBEX SETPATH packet 4049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] replyData = new byte[totalLength]; 4059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly replyData[0] = (byte)code; 4069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly replyData[1] = (byte)(totalLength >> 8); 4079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly replyData[2] = (byte)totalLength; 4089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (head != null) { 4099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(head, 0, replyData, 3, head.length); 4109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 4129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Write the OBEX SETPATH packet to the server. Byte 0: response code 4139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Byte 1&2: Connect Packet Length Byte 3 to n: headers 4149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 4159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.write(replyData); 4169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.flush(); 4179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 4209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Handles a disconnect request from a client. This method will read the 4219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * rest of the request from the client. Assuming the request is valid, it 4229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * will create a <code>HeaderSet</code> object to pass to the 4239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerRequestHandler</code> object. After the handler processes the 4249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * request, this method will create a reply message to send to the server. 4259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 4262e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @throws IOException 4279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if an error occurred at the transport layer 4289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 4299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private void handleDisconnectRequest() throws IOException { 4309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int length; 4319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int code = ResponseCodes.OBEX_HTTP_OK; 4329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int totalLength = 3; 4339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] head = null; 4349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int bytesReceived; 4359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly HeaderSet request = new HeaderSet(); 4369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly HeaderSet reply = new HeaderSet(); 4379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly length = input.read(); 4399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly length = (length << 8) + input.read(); 4409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4412e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly if (length > ObexHelper.MAX_PACKET_SIZE_INT) { 4429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE; 4439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength = 3; 4449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 4459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (length > 3) { 4469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] headers = new byte[length - 3]; 4479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly bytesReceived = input.read(headers); 4489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly while (bytesReceived != headers.length) { 4509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly bytesReceived += input.read(headers, bytesReceived, headers.length 4519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly - bytesReceived); 4529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4542e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly ObexHelper.updateHeaderSet(request, headers); 4559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.connectionID != null) { 4582e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly listener.setConnectionID(ObexHelper.convertToLong(request.connectionID)); 4599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 4609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly listener.setConnectionID(1); 4619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.authResp != null) { 4649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (!handleAuthResp(request.authResp)) { 4659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED; 4662e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly listener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01, 4679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authResp)); 4689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authResp = null; 4709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) { 4739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.authChall != null) { 4759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly handleAuthChall(request); 4769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authChall = null; 4779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 4809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly listener.onDisconnect(request, reply); 4819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 4829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null); 4839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return; 4849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 4859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 4879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Since a client will never response to an authentication 4889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * challenge on a DISCONNECT, there is no reason to keep track 4899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * of the challenge. 4909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 4919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if (reply.nonce != null) { challengeDigest = new byte[16]; 4929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); } 4939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * else { challengeDigest = null; } 4949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 4959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 4969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly long id = listener.getConnectionID(); 4979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (id == -1) { 4989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly reply.connectionID = null; 4999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 5002e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly reply.connectionID = ObexHelper.convertToByteArray(id); 5019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5032e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly head = ObexHelper.createHeader(reply, false); 5049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength += head.length; 5059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (totalLength > maxPacketLength) { 5079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength = 3; 5089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly head = null; 5099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 5109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Compute Length of OBEX CONNECT packet 5159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] replyData; 5169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (head != null) { 5179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly replyData = new byte[3 + head.length]; 5189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 5199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly replyData = new byte[3]; 5209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly replyData[0] = (byte)code; 5229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly replyData[1] = (byte)(totalLength >> 8); 5239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly replyData[2] = (byte)totalLength; 5249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (head != null) { 5259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(head, 0, replyData, 3, head.length); 5269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 5289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Write the OBEX DISCONNECT packet to the server. Byte 0: response code 5299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Byte 1&2: Connect Packet Length Byte 3 to n: headers 5309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 5319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.write(replyData); 5329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.flush(); 5339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 5369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Handles a connect request from a client. This method will read the rest 5379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * of the request from the client. Assuming the request is valid, it will 5389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * create a <code>HeaderSet</code> object to pass to the 5399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>ServerRequestHandler</code> object. After the handler processes the 5409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * request, this method will create a reply message to send to the server 5419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * with the response code provided. 5429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 5432e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @throws IOException 5449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if an error occurred at the transport layer 5459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 5469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private void handleConnectRequest() throws IOException { 5479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int packetLength; 5489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int version; 5499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int flags; 5509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int totalLength = 7; 5519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] head = null; 5529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int code = -1; 5539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly HeaderSet request = new HeaderSet(); 5549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly HeaderSet reply = new HeaderSet(); 5559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly int bytesReceived; 5569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 5589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Read in the length of the OBEX packet, OBEX version, flags, and max 5599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * packet length 5609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 5619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly packetLength = input.read(); 5629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly packetLength = (packetLength << 8) + input.read(); 5639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly version = input.read(); 5649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly flags = input.read(); 5659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly maxPacketLength = input.read(); 5669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly maxPacketLength = (maxPacketLength << 8) + input.read(); 5679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // should we check it? 5692e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly if (maxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) { 5702e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly maxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT; 5719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5732e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) { 5749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE; 5759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength = 7; 5769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 5779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (packetLength > 7) { 5789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] headers = new byte[packetLength - 7]; 5799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly bytesReceived = input.read(headers); 5809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly while (bytesReceived != headers.length) { 5829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly bytesReceived += input.read(headers, bytesReceived, headers.length 5839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly - bytesReceived); 5849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5862e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly ObexHelper.updateHeaderSet(request, headers); 5879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.connectionID != null) { 5902e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly listener.setConnectionID(ObexHelper.convertToLong(request.connectionID)); 5919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 5929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly listener.setConnectionID(1); 5939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 5949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.authResp != null) { 5969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (!handleAuthResp(request.authResp)) { 5979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED; 5982e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly listener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01, 5999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authResp)); 6009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authResp = null; 6029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) { 6059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (request.authChall != null) { 6069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly handleAuthChall(request); 6079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly reply.authResp = new byte[request.authResp.length]; 6089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length); 6099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authChall = null; 6109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly request.authResp = null; 6119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 6149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = listener.onConnect(request, reply); 6159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = validateResponseCode(code); 6169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (reply.nonce != null) { 6189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly challengeDigest = new byte[16]; 6199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); 6209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 6219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly challengeDigest = null; 6229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly long id = listener.getConnectionID(); 6249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (id == -1) { 6259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly reply.connectionID = null; 6269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 6272e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly reply.connectionID = ObexHelper.convertToByteArray(id); 6289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6302e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly head = ObexHelper.createHeader(reply, false); 6319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength += head.length; 6329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (totalLength > maxPacketLength) { 6349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength = 7; 6359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly head = null; 6369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 6379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 6399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly e.printStackTrace(); 6409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly totalLength = 7; 6419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly head = null; 6429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 6439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Compute Length of OBEX CONNECT packet 6492e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly byte[] length = ObexHelper.convertToByteArray(totalLength); 6509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 6529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Write the OBEX CONNECT packet to the server. Byte 0: response code 6539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number 6549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX 6559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers 6569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 6579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] sendData = new byte[totalLength]; 6589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendData[0] = (byte)code; 6599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendData[1] = length[2]; 6609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendData[2] = length[3]; 6619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendData[3] = (byte)0x10; 6629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly sendData[4] = (byte)0x00; 6632e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly sendData[5] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8); 6642e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly sendData[6] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF); 6659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (head != null) { 6679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(head, 0, sendData, 7, head.length); 6689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.write(sendData); 6719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.flush(); 6729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 6759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Closes the server session - in detail close I/O streams and the 6769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * underlying transport layer. Internal flag is also set so that later 6779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * attempt to read/write will throw an exception. 6789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 6799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public synchronized void close() { 6809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (listener != null) { 6819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly listener.onClose(); 6829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 6849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly input.close(); 6859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output.close(); 6869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly client.close(); 6879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly isClosed = true; 6889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 6899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly client = null; 6919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly input = null; 6929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly output = null; 6939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly listener = null; 6949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 6959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 6969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 6979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Verifies that the response code is valid. If it is not valid, it will 6989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code. 6999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 7009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param code 7019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the response code to check 7029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 7039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code> 7049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if <code>code</code> is not valid 7059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 7069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly private int validateResponseCode(int code) { 7079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 7089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) { 7099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return code; 7109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 7119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE) 7129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) { 7139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return code; 7149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 7159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST) 7169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) { 7179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return code; 7189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 7199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR) 7209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly && (code <= ResponseCodes.OBEX_HTTP_VERSION)) { 7219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return code; 7229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 7239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((code >= ResponseCodes.OBEX_DATABASE_FULL) 7249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) { 7259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return code; 7269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 7279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 7289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 7299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 7309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 7319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Called when the server received an authentication challenge header. This 7329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * will cause the authenticator to handle the authentication challenge. 7339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 7349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param header 7359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the header with the authentication challenge 7369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 7379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @return <code>true</code> if the last request should be resent; 7389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <code>false</code> if the last request should not be resent 7399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 7409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly protected boolean handleAuthChall(HeaderSet header) { 7419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (authenticator == null) { 7429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 7439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 7449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 7459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 7469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * An authentication challenge is made up of one required and two 7479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * optional tag length value triplets. The tag 0x00 is required to be in 7489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the authentication challenge and it represents the challenge digest 7499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * that was received. The tag 0x01 is the options tag. This tag tracks 7509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * if user ID is required and if full access will be granted. The tag 7519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 0x02 is the realm, which provides a description of which user name 7529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * and password to use. 7539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 7542e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.authChall); 7552e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly byte[] option = ObexHelper.getTagValue((byte)0x01, header.authChall); 7562e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly byte[] description = ObexHelper.getTagValue((byte)0x02, header.authChall); 7579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 7589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly String realm = ""; 7599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (description != null) { 7609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] realmString = new byte[description.length - 1]; 7619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(description, 1, realmString, 0, realmString.length); 7629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 7639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly switch (description[0] & 0xFF) { 7649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 7659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x00: 7669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ASCII encoding 7679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 7689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x01: 7699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-1 encoding 7709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 7719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly realm = new String(realmString, "ISO8859_1"); 7729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 7739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly throw new RuntimeException("Unsupported Encoding Scheme"); 7749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 7759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly break; 7769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 7779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0xFF: 7789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // UNICODE Encoding 7792e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly realm = ObexHelper.convertToUnicode(realmString, false); 7809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly break; 7819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 7829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x02: 7839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-2 encoding 7849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 7859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x03: 7869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-3 encoding 7879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 7889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x04: 7899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-4 encoding 7909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 7919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x05: 7929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-5 encoding 7939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 7949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x06: 7959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-6 encoding 7969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 7979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x07: 7989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-7 encoding 7999439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 8009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x08: 8019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-8 encoding 8029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 8039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly case 0x09: 8049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // ISO-8859-9 encoding 8059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Fall through 8069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly default: 8079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly throw new RuntimeException("Unsupported Encoding Scheme"); 8089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly boolean isUserIDRequired = false; 8129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly boolean isFullAccess = true; 8139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (option != null) { 8149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((option[0] & 0x01) != 0) { 8159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly isUserIDRequired = true; 8169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if ((option[0] & 0x02) != 0) { 8199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly isFullAccess = false; 8209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly PasswordAuthentication result = null; 8249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authChall = null; 8259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly try { 8279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly result = authenticator.onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess); 8289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } catch (Exception e) { 8299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 8309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 8339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * If no password is provided then we not resent the request 8349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 8359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (result == null) { 8369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 8379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] password = result.getPassword(); 8409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (password == null) { 8419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 8429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] userName = result.getUserName(); 8459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /* 8479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Create the authentication response header. It includes 1 required and 8489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 2 option tag length value triples. The required triple has a tag of 8499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 0x00 and is the response digest. The first optional tag is 0x01 and 8509439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * represents the user ID. If no user ID is provided, then no user ID 8519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * will be sent. The second optional tag is 0x02 and is the challenge 8529439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * that was received. This will always be sent 8539439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 8549439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (userName != null) { 8559439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authResp = new byte[38 + userName.length]; 8569439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authResp[36] = (byte)0x01; 8579439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authResp[37] = (byte)userName.length; 8589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(userName, 0, header.authResp, 38, userName.length); 8599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } else { 8609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authResp = new byte[36]; 8619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Create the secret String 8649439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] digest = new byte[challenge.length + password.length + 1]; 8659439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(challenge, 0, digest, 0, challenge.length); 8669439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Insert colon between challenge and password 8679439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly digest[challenge.length] = (byte)0x3A; 8689439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(password, 0, digest, challenge.length + 1, password.length); 8699439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8709439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Add the Response Digest 8719439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authResp[0] = (byte)0x00; 8729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authResp[1] = (byte)0x10; 8739439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8742e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.authResp, 2, 16); 8759439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8769439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Add the challenge 8779439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authResp[18] = (byte)0x02; 8789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly header.authResp[19] = (byte)0x10; 8799439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(challenge, 0, header.authResp, 20, 16); 8809439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8819439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return true; 8829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 8849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly /** 8859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Called when the server received an authentication response header. This 8869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * will cause the authenticator to handle the authentication response. 8879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 8889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @param authResp 8899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the authentication response 8909439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 8919439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * @return <code>true</code> if the response passed; <code>false</code> if 8929439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * the response failed 8939439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 8949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly protected boolean handleAuthResp(byte[] authResp) { 8959439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (authenticator == null) { 8969439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 8979439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 8989439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // get the correct password from the application 8992e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly byte[] correctPassword = authenticator.onAuthenticationResponse(ObexHelper.getTagValue( 9009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly (byte)0x01, authResp)); 9019439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (correctPassword == null) { 9029439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 9039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 9049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 9059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly byte[] temp = new byte[correctPassword.length + 16]; 9069439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 9079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(challengeDigest, 0, temp, 0, 16); 9089439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length); 9099439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 9102e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly byte[] correctResponse = ObexHelper.computeMd5Hash(temp); 9112e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp); 9129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 9139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // compare the MD5 hash array . 9149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly for (int i = 0; i < 16; i++) { 9159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (correctResponse[i] != actualResponse[i]) { 9169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return false; 9179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 9189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 9199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 9209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return true; 9219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 9229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly} 923