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