/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.kvem.jsr082.obex;
import javax.obex.Authenticator;
import javax.obex.ServerRequestHandler;
import javax.obex.ResponseCodes;
import javax.obex.HeaderSet;
import java.io.IOException;
import javax.microedition.io.Connection;
class ServerConnectionImpl extends ObexPacketStream
implements Connection, Runnable {
/** Debug information, should be false for RR. */
private static final boolean DEBUG = false;
ServerRequestHandler handler;
private int owner;
private boolean isConnected;
private long connId;
/** Current operation header size overflow flag. */
boolean operationHeadersOverflow = false;
/** Current operation state. */
boolean operationClosed;
void headerTooLarge() throws IOException {
operationHeadersOverflow = true;
operationClosed = true;
}
ServerConnectionImpl(ObexTransport transport,
ServerRequestHandler handler, Authenticator auth)
throws IOException {
super(transport);
this.handler = handler;
isClient = false;
authenticator = auth;
// owner field of all created HeaderSets
owner = HeaderSetImpl.OWNER_SERVER;
new Thread(this).start();
}
public void run() {
try {
while (processRequest()) {
// if connection closed
if (isClosed()) {
break;
}
}
} catch (Throwable t) {
if (DEBUG) {
System.out.println("ServerConnectionImpl thread exception");
t.printStackTrace();
}
}
if (DEBUG) {
System.out.println("ServerConnectionImpl: client disconnected");
}
close();
}
/**
* Modified sendPacket() function.
* If packet is too large - sends OBEX_HTTP_REQ_TOO_LARGE response.
*/
private int sendResponsePacket(byte[] head,
HeaderSetImpl headers) throws IOException {
int status = head[0] & 0xFF;
packetBegin(head);
packetAddConnectionID(getConnectionID(), headers);
packetAddAuthResponses();
packetAddHeaders(headers);
if (!queuedHeaders.isEmpty() || operationHeadersOverflow) {
queuedHeaders.removeAllElements();
setPacketType(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE);
}
packetEnd();
return status;
}
private void doConnect() throws IOException {
// NOTE client may not authenticated the server and wants to
// so allow multiple connect requests
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
HeaderSetImpl responseHeaderSet = new HeaderSetImpl(owner);
// server side check
if (buffer[3] != 0x10 || packetLength < 7) {
// IMPL_NOTE: It is not decided what to do if the OBEX version number
// is different from the one we support (which is presumably 1.0).
// Windows uses version 1.2, Linux uses version 1.1, and we
// probably want to work with both.
// throw new IOException("unsupported client obex version");
}
// ignore flags
// save maximum client supported packet size
maxSendLength = decodeLength16(5);
if (maxSendLength > OBEX_MAXIMUM_PACKET_LENGTH) {
maxSendLength = OBEX_MAXIMUM_PACKET_LENGTH;
}
parsePacketHeaders(inputHeaderSet, 7);
// processMissingAuthentications();
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
try {
status = handler.onConnect(inputHeaderSet,
responseHeaderSet);
} catch (Throwable t) {
t.printStackTrace();
}
status = validateStatus(status);
if (status != ResponseCodes.OBEX_HTTP_OK) {
// lets client will authenticate first
authResponses.removeAllElements();
}
byte[] head = new byte[] {
(byte)status,
0, 0, // length will be here
0x10, // version 1.0 of OBEX
0x0, // flags
(byte) (OBEX_MAXIMUM_PACKET_LENGTH / 0x100), // maximum client
(byte) (OBEX_MAXIMUM_PACKET_LENGTH % 0x100), // supported packet
// length
};
status = sendResponsePacket(head, responseHeaderSet);
if (status == ResponseCodes.OBEX_HTTP_OK) {
isConnected = true;
}
}
private boolean notConnected() throws IOException {
if (!isConnected) {
HeaderSetImpl headers = new HeaderSetImpl(owner);
headers.setHeader(HeaderSet.DESCRIPTION, "not connected");
sendPacket(PACKET_BAD_REQUEST, getConnectionID(), headers, true);
return true;
}
return false;
}
void onAuthenticationFailure(byte[] username) throws IOException {
try {
if (DEBUG) {
System.out.println("ServerConnectionImpl:"
+ " handler.onAuthenticationFailure()");
}
handler.onAuthenticationFailure(username);
} catch (Throwable t) {
t.printStackTrace();
}
operationClosed = true;
}
private void doDisconnect() throws IOException {
if (notConnected()) {
return;
}
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
HeaderSetImpl responseHeaderSet = new HeaderSetImpl(owner);
parsePacketHeaders(inputHeaderSet, 3);
// processMissingAuthentications();
try {
handler.onDisconnect(inputHeaderSet, responseHeaderSet);
} catch (Throwable t) {
t.printStackTrace();
}
byte[] head = new byte[] {
(byte) ResponseCodes.OBEX_HTTP_OK,
0, 0, // length will be here
};
int status = sendResponsePacket(head, responseHeaderSet);
if (status == ResponseCodes.OBEX_HTTP_OK) {
isConnected = false;
}
}
private void doPut(HeaderSetImpl inputHeaderSet) throws IOException {
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
ServerOperation op =
new ServerOperation(this, inputHeaderSet);
try {
status = handler.onPut(op);
} catch (Throwable t) {
t.printStackTrace();
}
status = validateStatus(status);
if (operationHeadersOverflow) {
status = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
}
op.destroy(status);
}
private void doDelete(HeaderSetImpl inputHeaderSet) throws IOException {
HeaderSetImpl responseHeaderSet = new HeaderSetImpl(owner);
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
try {
status = handler.onDelete(inputHeaderSet, responseHeaderSet);
} catch (Throwable t) {
t.printStackTrace();
}
status = validateStatus(status);
byte[] head = new byte[] {
(byte)status,
0, 0, // length will be here
};
sendResponsePacket(head, responseHeaderSet);
}
private void doPutOrDelete() throws IOException {
if (notConnected()) {
return;
}
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
int mode = ServerOperation.waitForData(this,
inputHeaderSet, OPCODE_PUT);
switch (mode) {
case 0: sendPacket(PACKET_SUCCESS, getConnectionID(), null, true);
return;
case 1: doPut(inputHeaderSet); return;
case 2: doDelete(inputHeaderSet); return;
default:return;
}
}
private void doGet() throws IOException {
if (notConnected()) {
return;
}
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
ServerOperation op =
new ServerOperation(this);
try {
status = handler.onGet(op);
} catch (Throwable t) {
t.printStackTrace();
}
status = validateStatus(status);
if (operationHeadersOverflow) {
status = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
}
op.destroy(status);
}
private void doSetPath() throws IOException {
if (notConnected()) {
return;
}
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
HeaderSetImpl responseHeaderSet = new HeaderSetImpl(owner);
// check flags
boolean create = ((buffer[3] & 2) == 0);
boolean backup = ((buffer[3] & 1) == 1);
parsePacketHeaders(inputHeaderSet, 5);
// processMissingAuthentications();
int status = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
try {
status = handler.onSetPath(inputHeaderSet,
responseHeaderSet, backup, create);
} catch (Throwable t) {
t.printStackTrace();
}
status = validateStatus(status);
byte[] head = new byte[] {
(byte)status,
0, 0, // length will be here
};
sendResponsePacket(head, responseHeaderSet);
}
/**
* Process one client request
* @return false when connection closed
*/
private boolean processRequest() throws IOException {
try {
recvPacket();
} catch (IOException e) {
return false;
}
HeaderSetImpl inputHeaderSet = new HeaderSetImpl(owner);
operationHeadersOverflow = false;
operationClosed = false;
isEof = false;
switch (packetType) {
case OPCODE_CONNECT:
doConnect();
break;
case OPCODE_DISCONNECT:
doDisconnect();
break;
case OPCODE_PUT:
case OPCODE_PUT | OPCODE_FINAL:
doPutOrDelete();
break;
case OPCODE_GET:
case OPCODE_GET | OPCODE_FINAL:
doGet();
break;
case OPCODE_SETPATH:
doSetPath();
break;
case OPCODE_ABORT:
// ignore abort, it is too late, any of the operations is
// finished
byte[] head = new byte[] {
(byte) ResponseCodes.OBEX_HTTP_OK,
0, 0, // length will be here
};
sendResponsePacket(head, null);
break;
default:
// wrong packet received, ignoring
if (DEBUG) {
System.out.println("Wrong packet: id = "
+ inputHeaderSet.packetType + " length = "
+ packetLength);
}
sendPacket(PACKET_NOT_IMPLEMENTED, getConnectionID(),
null, true);
}
return true;
}
public void setConnectionID(long id) {
try { // may by overloaded by user and throw exception
connId = id;
handler.setConnectionID(id);
} catch (Throwable e) {
// nothing
}
}
public long getConnectionID() {
try { // may by overloaded by user and throw exception
long id = handler.getConnectionID();
if (connId == id) {
return -1;
}
connId = id;
return id;
} catch (Throwable e) {
return -1;
}
}
}
|