001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
003: * Reserved. Use is subject to license terms.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: /*
026: *
027: *
028: * Created on Jan 29, 2004
029: *
030: */
031: package gov.nist.microedition.sip;
032:
033: import gov.nist.siplite.message.*;
034: import gov.nist.siplite.address.URI;
035: import gov.nist.siplite.header.Header;
036: import gov.nist.siplite.header.AuthorizationHeader;
037: import gov.nist.siplite.header.ProxyAuthorizationHeader;
038: import gov.nist.siplite.header.SubscriptionStateHeader;
039: import gov.nist.siplite.stack.Dialog;
040: import gov.nist.siplite.stack.Subscription;
041: import gov.nist.siplite.parser.Lexer;
042: import javax.microedition.sip.SipClientConnection;
043: import javax.microedition.sip.SipClientConnectionListener;
044: import javax.microedition.sip.SipConnection;
045: import javax.microedition.sip.SipConnectionNotifier;
046: import javax.microedition.sip.SipDialog;
047: import javax.microedition.sip.SipException;
048: import com.sun.midp.security.SecurityToken;
049:
050: /**
051: * SIP Dialog implementation.
052: *
053: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
054: */
055: public class SipDialogImpl implements SipDialog {
056: /**
057: * Initialized, initial state of dialog. This state is initialized
058: * in this class instead of SipDialog interface as it is an internal
059: * state
060: */
061: protected static final int INITIALIZED = -1;
062: /**
063: * current state of the dialog
064: */
065: private byte state;
066: /**
067: * dialogID (Call-ID + remote tag + local tag).
068: */
069: private String dialogID = null;
070: /**
071: * This implementation of dialog is linked to the Nist-Siplite dialog
072: */
073: protected Dialog dialog = null;
074: /**
075: * Current handle to asynchronous notifier.
076: */
077: private SipConnectionNotifier sipConnectionNotifier = null;
078: /**
079: * Handle for current listener.
080: */
081: private SipClientConnectionListener sipClientConnectionListener = null;
082: /** Proxy server autorization headers. */
083: protected ProxyAuthorizationHeader proxyAuthorizationHeader = null;
084: /** Authorization header key. */
085: protected AuthorizationHeader authorizationHeader = null;
086: /**
087: * Security token for SIP/SIPS protocol class
088: */
089: private SecurityToken classSecurityToken;
090: /**
091: * The refresh ID of the refresh task associated with this client connection
092: * if there is any
093: */
094: private String refreshID = null;
095: /**
096: * Permission check before sending a PRACK request.
097: */
098: protected boolean isReliableProvReceived = false;
099: /**
100: * True if this dialog can't be terminated until BYE is received.
101: */
102: private boolean waitForBye = false;
103:
104: /**
105: * Constructs this dialog based upon the Nist-Siplite dialog
106: * @param dialog Nist-Siplite dialog
107: * @param sipConnectionNotifier the notification handler
108: * @param classSecurityToken Security token for SIP/SIPS protocol class
109: * with this client connection
110: */
111: protected SipDialogImpl(Dialog dialog,
112: SipConnectionNotifier sipConnectionNotifier,
113: SecurityToken classSecurityToken) {
114: state = INITIALIZED;
115:
116: if (dialog != null) {
117: dialogID = dialog.getDialogId();
118: int underlyingDlgState = dialog.getState();
119:
120: if (underlyingDlgState == Dialog.CONFIRMED_STATE) {
121: state = CONFIRMED;
122: } else if (underlyingDlgState == Dialog.COMPLETED_STATE
123: || underlyingDlgState == Dialog.TERMINATED_STATE) {
124: state = TERMINATED;
125: }
126: }
127:
128: this .dialog = dialog;
129: this .sipConnectionNotifier = sipConnectionNotifier;
130: this .classSecurityToken = classSecurityToken;
131: }
132:
133: /**
134: * Returns new SipClientConnection in this dialog.
135: * The SipClientConnection will be pre-initialized with the given
136: * method and
137: * following headers will be set at least
138: * (for details see RFC 3261 [1] 12.2.1.1 Generating the Request, p.73):
139: * <pre>
140: * To
141: * From
142: * CSeq
143: * Call-ID
144: * Max-Forwards
145: * Via
146: * Contact
147: * Route//ifthedialogrouteisnotempty
148: * </pre>
149: * @param method - given method
150: * @return SipClientConnection with preset headers.
151: * @throws IllegalArgumentException - if the method is invalid
152: * @throws SipException - INVALID_STATE if the new connection can not be
153: * established in the current state of dialog.
154: */
155: public SipClientConnection getNewClientConnection(String method)
156: throws IllegalArgumentException, SipException {
157:
158: // JSR180: all methods are available in CONFIRMED and EARLY states.
159: if (state != SipDialog.CONFIRMED && state != SipDialog.EARLY) {
160: throw new SipException("the client connection can not "
161: + "be initialized, because of wrong state.",
162: SipException.INVALID_STATE);
163: }
164:
165: // Validating the method.
166: if (!Lexer.isValidName(method)) {
167: throw new IllegalArgumentException("Invalid method: '"
168: + method + "'");
169: }
170:
171: // Create the new sip client connection
172: // and init the request
173: SipClientConnection sipClientConnection = new SipClientConnectionImpl(
174: getDialog().getRemoteTarget().getURI(), this );
175: // ((SipClientConnectionImpl)sipClientConnection).start();
176:
177: if ((sipConnectionNotifier != null)
178: && !((SipConnectionNotifierImpl) sipConnectionNotifier)
179: .isConnectionOpen()) {
180: sipConnectionNotifier = null;
181: }
182:
183: sipClientConnection.initRequest(method.toUpperCase().trim(),
184: sipConnectionNotifier);
185:
186: // keep a trace of the connection created
187: return sipClientConnection;
188: }
189:
190: /**
191: * Does the given SipConnection belong to this dialog.
192: * @param sc - SipConnection to be checked, can be either
193: * SipClientConnection or SipServerConnection
194: * @return true if the SipConnection belongs to the this dialog.
195: * Returns false
196: * if the connection is not part of this dialog or the dialog is terminated.
197: */
198: public boolean isSameDialog(SipConnection sc) {
199: if (state == SipDialog.TERMINATED || sc == null) {
200: return false;
201: }
202:
203: SipDialog dlg = sc.getDialog();
204:
205: if (dlg != null) {
206: String id = dlg.getDialogID();
207: if (id != null && id.equals(dialogID)) {
208: return true;
209: }
210: }
211:
212: return false;
213: }
214:
215: /**
216: * Returns the state of the SIP Dialog.
217: * @return dialog state byte number.
218: */
219: public byte getState() {
220: return state;
221: }
222:
223: /**
224: * Returns the ID of the SIP Dialog.
225: * @return Dialog ID (Call-ID + remote tag + local tag).
226: * Returns null if the dialog is terminated.
227: */
228: public String getDialogID() {
229: if (state == TERMINATED) {
230: return null;
231: }
232: return dialogID;
233: }
234:
235: /**
236: * Sets the Dialog identifier.
237: * @param newDialogID dialog identifier
238: */
239: protected void setDialogID(String newDialogID) {
240: dialogID = newDialogID;
241: }
242:
243: /**
244: * Sets the current Dialog handler.
245: * @param newDialog the new Dialog
246: */
247: protected void setDialog(Dialog newDialog) {
248: dialog = newDialog;
249: setDialogID(newDialog.getDialogId());
250: }
251:
252: /**
253: * Gets the curre SIP Dialog.
254: * @return the current Dialog handle
255: */
256: protected Dialog getDialog() {
257: return dialog;
258: }
259:
260: /**
261: * Sets the current connection listener.
262: * @param newSipClientConnectionListener the new listener
263: */
264: protected void setSipClientConnectionListener(
265: SipClientConnectionListener newSipClientConnectionListener) {
266: sipClientConnectionListener = newSipClientConnectionListener;
267: }
268:
269: /**
270: * Gets the current listener.
271: * @return the current listener
272: */
273: protected SipClientConnectionListener getSipClientConnectionListener() {
274: return sipClientConnectionListener;
275: }
276:
277: /**
278: * Changes the state of this dialog
279: * @param newState the new state of this dialog
280: */
281: protected void setState(byte newState) {
282: state = newState;
283: }
284:
285: /**
286: * Changes the state of this dialog to TERMINATED
287: * if there are no active subscriptions.
288: */
289: protected void terminateIfNoSubscriptions() {
290: if (dialog == null) {
291: setState(TERMINATED);
292: return;
293: }
294:
295: if (dialog.subscriptionList.isEmpty() && !waitForBye) {
296: // TERMINATE the dialog
297: setState(TERMINATED);
298: }
299: }
300:
301: /**
302: * Adds a new subscription to the list of active subscriptions.
303: * @param s a subscription to add
304: */
305: protected void addSubscription(Subscription s) {
306: dialog.subscriptionList.addSubscription(s);
307: }
308:
309: /**
310: * Removes the subscription matching the given response or NOTIFY
311: * from the list of active subscriptions.
312: * @param message response or NOTIFY message
313: */
314: protected void removeSubscription(Message message) {
315: Subscription s = dialog.subscriptionList
316: .getMatchingSubscription(message);
317: if (s != null) {
318: dialog.subscriptionList.removeSubscription(s);
319: }
320: }
321:
322: /**
323: * Accessor for 'waitForBye' field.
324: * @param bye true if we have to wait until 'BYE' is received
325: * to terminate the dialog, false otherwise
326: */
327: protected void setWaitForBye(boolean bye) {
328: waitForBye = bye;
329: }
330:
331: /**
332: * Handles NOTIFY request.
333: * @param request NOTIFY message
334: * @param newDialog a new underlying dialog implementation
335: * to associate with this dialog, may be null
336: * @param newDialogId a new dialog id to set for this dialog
337: */
338: protected void handleNotify(Request request, Dialog newDialog,
339: String newDialogId) {
340: SubscriptionStateHeader ssh = (SubscriptionStateHeader) request
341: .getHeader(Header.SUBSCRIPTION_STATE);
342:
343: if (ssh != null && ssh.isTerminated()) {
344: Subscription s = dialog.subscriptionList
345: .getMatchingSubscription(request);
346:
347: if (s != null) {
348: dialog.subscriptionList.removeSubscription(s);
349: }
350:
351: if (dialog.isSubscribeDialog() || dialog.isInviteDialog()) {
352: // IMPL_NOTE: currently we don't handle the following scenario:
353: //
354: // INVITE/200OK - INVITE/200OK - SUBSCRIBE/200OK - BYE/200OK -
355: // NOTIFY(terminate subscription)/200OK - (*) ...
356: //
357: // At the point (*) the dialog will be in TERMINATED state
358: // what is incorrect.
359: terminateIfNoSubscriptions();
360: }
361: } else {
362: setState(CONFIRMED);
363:
364: if (newDialog != null) {
365: setDialog(newDialog);
366: } else {
367: setDialogID(newDialogId);
368: }
369: }
370: }
371:
372: /**
373: * Gets the current security token.
374: * @return the current security token
375: */
376: protected SecurityToken getSecurityToken() {
377: return classSecurityToken;
378: }
379:
380: /**
381: * Gets the current refreshID.
382: * @return the current refreshID
383: */
384: protected String getRefreshID() {
385: return refreshID;
386: }
387:
388: /**
389: * Sets the current refreshID.
390: * @param newRefreshID new refreshID value
391: */
392: protected void setRefreshID(String newRefreshID) {
393: refreshID = newRefreshID;
394: }
395: }
|