FileDocCategorySizeDatePackage
SipDialogImpl.javaAPI DocphoneME MR2 API (J2ME)12610Wed May 02 18:00:42 BST 2007gov.nist.microedition.sip

SipDialogImpl.java

/*
 * Portions Copyright  2000-2007 Sun Microsystems, Inc. All Rights
 * Reserved.  Use is subject to license terms.
 * 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.
 */
/*
 *   
 *
 * Created on Jan 29, 2004
 *
 */
package gov.nist.microedition.sip;

import gov.nist.siplite.message.*;
import gov.nist.siplite.address.URI;
import gov.nist.siplite.header.Header;
import gov.nist.siplite.header.AuthorizationHeader;
import gov.nist.siplite.header.ProxyAuthorizationHeader;
import gov.nist.siplite.header.SubscriptionStateHeader;
import gov.nist.siplite.stack.Dialog;
import gov.nist.siplite.stack.Subscription;
import gov.nist.siplite.parser.Lexer;
import javax.microedition.sip.SipClientConnection;
import javax.microedition.sip.SipClientConnectionListener;
import javax.microedition.sip.SipConnection;
import javax.microedition.sip.SipConnectionNotifier;
import javax.microedition.sip.SipDialog;
import javax.microedition.sip.SipException;
import com.sun.midp.security.SecurityToken;

/**
 * SIP Dialog implementation.
 *
 * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
 */
public class SipDialogImpl implements SipDialog {
    /**
     * Initialized, initial state of dialog. This state is initialized
     * in this class instead of SipDialog interface as it is an internal
     * state
     */
    protected static final int INITIALIZED = -1;
    /**
     * current state of the dialog
     */
    private byte state;
    /**
     * dialogID (Call-ID + remote tag + local tag).
     */
    private String dialogID = null;
    /**
     * This implementation of dialog is linked to the Nist-Siplite dialog
     */
    protected Dialog dialog = null;
    /**
     * Current handle to asynchronous notifier.
     */
    private SipConnectionNotifier sipConnectionNotifier = null;
    /**
     * Handle for current listener.
     */
    private SipClientConnectionListener sipClientConnectionListener = null;
    /** Proxy server autorization headers. */
    protected ProxyAuthorizationHeader proxyAuthorizationHeader = null;
    /** Authorization header key. */
    protected AuthorizationHeader authorizationHeader = null;
    /**
     * Security token for SIP/SIPS protocol class
     */
    private SecurityToken classSecurityToken;
    /**
     * The refresh ID of the refresh task associated with this client connection
     * if there is any
     */
    private String refreshID = null;
    /**
     * Permission check before sending a PRACK request.
     */
    protected boolean isReliableProvReceived = false;
    /**
     * True if this dialog can't be terminated until BYE is received.
     */
    private boolean waitForBye = false;

    /**
     * Constructs this dialog based upon the Nist-Siplite dialog
     * @param dialog Nist-Siplite dialog
     * @param sipConnectionNotifier the notification handler
     * @param classSecurityToken Security token for SIP/SIPS protocol class
     * with this client connection
     */
    protected SipDialogImpl(Dialog dialog,
            SipConnectionNotifier sipConnectionNotifier,
            SecurityToken classSecurityToken) {
        state = INITIALIZED;

        if (dialog != null) {
            dialogID = dialog.getDialogId();
            int underlyingDlgState = dialog.getState();

            if (underlyingDlgState == Dialog.CONFIRMED_STATE) {
                state = CONFIRMED;
            } else if (underlyingDlgState == Dialog.COMPLETED_STATE ||
                       underlyingDlgState == Dialog.TERMINATED_STATE) {
                state = TERMINATED;
            }
        }

        this.dialog = dialog;
        this.sipConnectionNotifier = sipConnectionNotifier;
        this.classSecurityToken = classSecurityToken;
    }

    /**
     * Returns new SipClientConnection in this dialog.
     * The SipClientConnection will be pre-initialized with the given
     * method and
     * following headers will be set at least
     * (for details see RFC 3261 [1] 12.2.1.1 Generating the Request, p.73):
     * <pre>
     * To
     * From
     * CSeq
     * Call-ID
     * Max-Forwards
     * Via
     * Contact
     * Route//ifthedialogrouteisnotempty
     * </pre>
     * @param method - given method
     * @return SipClientConnection with preset headers.
     * @throws IllegalArgumentException - if the method is invalid
     * @throws SipException - INVALID_STATE if the new connection can not be
     * established in the current state of dialog.
     */
    public SipClientConnection getNewClientConnection(String method)
            throws IllegalArgumentException, SipException {

        // JSR180: all methods are available in CONFIRMED and EARLY states.
        if (state != SipDialog.CONFIRMED && state != SipDialog.EARLY) {
            throw new SipException("the client connection can not "
                    + "be initialized, because of wrong state.",
                    SipException.INVALID_STATE);
        }

        // Validating the method.
        if (!Lexer.isValidName(method)) {
            throw new IllegalArgumentException("Invalid method: '" +
                method + "'");
        }

        // Create the new sip client connection
        // and init the request
        SipClientConnection sipClientConnection =
                new SipClientConnectionImpl(
                    getDialog().getRemoteTarget().getURI(), this);
        // ((SipClientConnectionImpl)sipClientConnection).start();

        if ((sipConnectionNotifier != null) &&
            !((SipConnectionNotifierImpl)sipConnectionNotifier).
                isConnectionOpen()) {
            sipConnectionNotifier = null;
        }

        sipClientConnection.initRequest(method.toUpperCase().trim(),
            sipConnectionNotifier);

        // keep a trace of the connection created
        return sipClientConnection;
    }

    /**
     * Does the given SipConnection belong to this dialog.
     * @param sc - SipConnection to be checked, can be either
     * SipClientConnection or SipServerConnection
     * @return true if the SipConnection belongs to the this dialog.
     * Returns false
     * if the connection is not part of this dialog or the dialog is terminated.
     */
    public boolean isSameDialog(SipConnection sc) {
        if (state == SipDialog.TERMINATED || sc == null) {
            return false;
        }

        SipDialog dlg = sc.getDialog();

        if (dlg != null) {
            String id = dlg.getDialogID();
            if (id != null && id.equals(dialogID)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns the state of the SIP Dialog.
     * @return dialog state byte number.
     */
    public byte getState() {
        return state;
    }

    /**
     * Returns the ID of the SIP Dialog.
     * @return Dialog ID (Call-ID + remote tag + local tag).
     * Returns null if the dialog is terminated.
     */
    public String getDialogID() {
        if (state  ==  TERMINATED) {
            return null;
        }
        return dialogID;
    }

    /**
     * Sets the Dialog identifier.
     * @param newDialogID dialog identifier
     */
    protected void setDialogID(String newDialogID) {
        dialogID = newDialogID;
    }

    /**
     * Sets the current Dialog handler.
     * @param newDialog the new Dialog
     */
    protected void setDialog(Dialog newDialog) {
        dialog = newDialog;
        setDialogID(newDialog.getDialogId());
    }

    /**
     * Gets the curre SIP Dialog.
     * @return the current Dialog handle
     */
    protected Dialog getDialog() {
        return dialog;
    }

    /**
     * Sets the current connection listener.
     * @param newSipClientConnectionListener the new listener
     */
    protected void setSipClientConnectionListener(
            SipClientConnectionListener newSipClientConnectionListener) {
        sipClientConnectionListener = newSipClientConnectionListener;
    }

    /**
     * Gets the current listener.
     * @return the current listener
     */
    protected SipClientConnectionListener getSipClientConnectionListener() {
        return sipClientConnectionListener;
    }

    /**
     * Changes the state of this dialog
     * @param newState the new state of this dialog
     */
    protected void setState(byte newState) {
        state = newState;
    }

    /**
     * Changes the state of this dialog to TERMINATED
     * if there are no active subscriptions.
     */
    protected void terminateIfNoSubscriptions() {
        if (dialog == null) {
            setState(TERMINATED);
            return;
        }

        if (dialog.subscriptionList.isEmpty() && !waitForBye) {
            // TERMINATE the dialog
            setState(TERMINATED);
        }
    }

    /**
     * Adds a new subscription to the list of active subscriptions.
     * @param s a subscription to add
     */
    protected void addSubscription(Subscription s) {
        dialog.subscriptionList.addSubscription(s);
    }

    /**
     * Removes the subscription matching the given response or NOTIFY
     * from the list of active subscriptions.
     * @param message response or NOTIFY message
     */
    protected void removeSubscription(Message message) {
        Subscription s =
            dialog.subscriptionList.getMatchingSubscription(message);
        if (s != null) {
            dialog.subscriptionList.removeSubscription(s);
        }
    }

    /**
     * Accessor for 'waitForBye' field.
     * @param bye true if we have to wait until 'BYE' is received
     * to terminate the dialog, false otherwise
     */
    protected void setWaitForBye(boolean bye) {
        waitForBye = bye;
    }

    /**
     * Handles NOTIFY request.
     * @param request NOTIFY message
     * @param newDialog a new underlying dialog implementation
     * to associate with this dialog, may be null
     * @param newDialogId a new dialog id to set for this dialog
     */
    protected void handleNotify(Request request,
            Dialog newDialog, String newDialogId) {
        SubscriptionStateHeader ssh = (SubscriptionStateHeader)
            request.getHeader(Header.SUBSCRIPTION_STATE);

        if (ssh != null && ssh.isTerminated()) {
            Subscription s =
                dialog.subscriptionList.getMatchingSubscription(request);

            if (s != null) {
                dialog.subscriptionList.removeSubscription(s);
            }

            if (dialog.isSubscribeDialog() || dialog.isInviteDialog()) {
                // IMPL_NOTE: currently we don't handle the following scenario:
                //
                // INVITE/200OK - INVITE/200OK - SUBSCRIBE/200OK - BYE/200OK -
                // NOTIFY(terminate subscription)/200OK - (*) ...
                //
                // At the point (*) the dialog will be in TERMINATED state
                // what is incorrect.
                terminateIfNoSubscriptions();
            }
        } else {
            setState(CONFIRMED);

            if (newDialog != null) {
                setDialog(newDialog);
            } else {
                setDialogID(newDialogId);
            }
        }
    }

    /**
     * Gets the current security token.
     * @return the current security token
     */
    protected SecurityToken getSecurityToken() {
        return classSecurityToken;
    }

    /**
     * Gets the current refreshID.
     * @return the current refreshID
     */
    protected String getRefreshID() {
        return refreshID;
    }

    /**
     * Sets the current refreshID.
     * @param newRefreshID new refreshID value
     */
    protected void setRefreshID(String newRefreshID) {
        refreshID = newRefreshID;
    }
}