FileDocCategorySizeDatePackage
ServerConnectionImpl.javaAPI DocphoneME MR2 API (J2ME)12615Wed May 02 18:00:32 BST 2007com.sun.kvem.jsr082.obex

ServerConnectionImpl.java

/*
 *   
 *
 * 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;
        }
    }
}