0001: /*
0002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
0003: * Reserved. Use is subject to license terms.
0004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU General Public License version
0008: * 2 only, as published by the Free Software Foundation.
0009: *
0010: * This program is distributed in the hope that it will be useful, but
0011: * WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * General Public License version 2 for more details (a copy is
0014: * included at /legal/license.txt).
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * version 2 along with this work; if not, write to the Free Software
0018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0019: * 02110-1301 USA
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0022: * Clara, CA 95054 or visit www.sun.com if you need additional
0023: * information or have any questions.
0024: */
0025: /*
0026: *
0027: *
0028: * Created on Jan 29, 2004
0029: *
0030: */
0031: package gov.nist.microedition.sip;
0032:
0033: import gov.nist.core.ParseException;
0034: import gov.nist.core.NameValue;
0035:
0036: import java.io.IOException;
0037: import java.io.OutputStream;
0038: import java.io.InputStream;
0039: import java.io.InterruptedIOException;
0040: import java.io.ByteArrayOutputStream;
0041: import java.io.ByteArrayInputStream;
0042:
0043: import java.util.Vector;
0044: import java.util.Enumeration;
0045:
0046: import javax.microedition.rms.RecordEnumeration;
0047: import javax.microedition.rms.RecordStore;
0048: import javax.microedition.rms.RecordStoreException;
0049:
0050: import javax.microedition.sip.SipConnection;
0051:
0052: import javax.microedition.sip.SipClientConnection;
0053: import javax.microedition.sip.SipClientConnectionListener;
0054: import javax.microedition.sip.SipConnectionNotifier;
0055: import javax.microedition.sip.SipDialog;
0056: import javax.microedition.sip.SipException;
0057: import javax.microedition.sip.SipRefreshListener;
0058:
0059: import gov.nist.siplite.TransactionUnavailableException;
0060: import gov.nist.siplite.SipStack;
0061: import gov.nist.siplite.address.Address;
0062: import gov.nist.siplite.address.SipURI;
0063: import gov.nist.siplite.address.URI;
0064: import gov.nist.siplite.message.*;
0065: import gov.nist.siplite.stack.Subscription;
0066: import gov.nist.siplite.stack.ClientTransaction;
0067: import gov.nist.siplite.stack.Dialog;
0068: import gov.nist.siplite.stack.authentication.Credentials;
0069: import gov.nist.siplite.stack.authentication.DigestClientAuthentication;
0070: import gov.nist.siplite.header.CSeqHeader;
0071: import gov.nist.siplite.header.CallIdHeader;
0072: import gov.nist.siplite.header.ContactHeader;
0073: import gov.nist.siplite.header.ContentLengthHeader;
0074: import gov.nist.siplite.header.ContactList;
0075: import gov.nist.siplite.header.ContentTypeHeader;
0076: import gov.nist.siplite.header.ExpiresHeader;
0077: import gov.nist.siplite.header.FromHeader;
0078: import gov.nist.siplite.header.Header;
0079: import gov.nist.siplite.header.HeaderList;
0080: import gov.nist.siplite.header.MaxForwardsHeader;
0081: import gov.nist.siplite.header.ToHeader;
0082: import gov.nist.siplite.header.ViaHeader;
0083: import gov.nist.siplite.header.SubscriptionStateHeader;
0084: import gov.nist.siplite.header.ParameterLessHeader;
0085: import gov.nist.siplite.header.ExtensionHeader;
0086: import gov.nist.siplite.SIPUtils;
0087: import gov.nist.siplite.SIPConstants;
0088: import gov.nist.core.NameValueList;
0089: import gov.nist.core.Utils;
0090: import gov.nist.core.Separators;
0091: import com.sun.midp.security.SecurityToken;
0092: import gov.nist.siplite.parser.Lexer;
0093:
0094: import com.sun.midp.log.Logging;
0095: import com.sun.midp.log.LogChannels;
0096:
0097: /**
0098: * Client SIP connection implementation.
0099: *
0100: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
0101: */
0102: public class SipClientConnectionImpl implements SipClientConnection {
0103: // Runnable {
0104: /**
0105: * Security token for SIP/SIPS protocol class
0106: */
0107: private SecurityToken classSecurityToken;
0108:
0109: // Client Transaction States
0110: /**
0111: * Terminated, the final state, in which the SIP connection has
0112: * been terminated by error or closed
0113: */
0114: public static final int TERMINATED = 0;
0115: /**
0116: * Created, SipClientConnection created from Connector or SipDialog
0117: */
0118: public static final int CREATED = 1;
0119: /**
0120: * Initialized, request has been initialized with initRequest(...)
0121: * or initAck() or initCancel()
0122: */
0123: public static final int INITIALIZED = 2;
0124: /**
0125: * Stream Open, OutputStream opened with openContentOutputStream().
0126: * Opening InputStream for received response does not trigger
0127: * state transition.
0128: */
0129: public static final int STREAM_OPEN = 3;
0130: /**
0131: * Proceeding, request has been sent, waiting for the response, or
0132: * provisional 1xx response received. initCancel() can be called,
0133: * which will spawn a new SipClientConnection which is in
0134: * Initialized state
0135: */
0136: public static final int PROCEEDING = 4;
0137: /**
0138: * Unauthorized, transaction completed with response 401
0139: * (Unauthorized) or 407 (Proxy Authentication Required). The
0140: * application can re-originate the request with proper
0141: * credentials by calling setCredentials() method. After this the
0142: * SipClientConnection is back in Proceeding state.
0143: */
0144: public static final int UNAUTHORIZED = 5;
0145: /**
0146: * Completed, transaction completed with final response
0147: * (2xx, 3xx, 4xx, 5xx, 6xx) in this state the ACK can be initialized.
0148: * Multiple 200 OK responses can be received. Note different state
0149: * transition for responses 401 and 407.
0150: */
0151: public static final int COMPLETED = 6;
0152:
0153: /**
0154: * Max length of queue of incoming responses.
0155: */
0156: private static final int MAX_NUM_RESPONSES = 10;
0157:
0158: /**
0159: * the sip dialog this client transaction belongs to
0160: */
0161: private SipDialog sipDialog = null;
0162:
0163: /**
0164: * Listener to notify when a response will be received
0165: */
0166: private SipClientConnectionListener sipClientConnectionListener = null;
0167: /**
0168: * The Sip Connection notifier associated with this client connection
0169: */
0170: private SipConnectionNotifier sipConnectionNotifier = null;
0171: /**
0172: * Callback interface for refresh events
0173: */
0174: private SipRefreshListener sipRefreshListener = null;
0175: /**
0176: * The refresh ID of the refresh task associated with this client
0177: * connection if there is any
0178: */
0179: private String refreshID = null;
0180: /**
0181: * current state of this client transaction
0182: */
0183: protected int state;
0184: /**
0185: * flag to know the state of the connection (open or close)
0186: */
0187: private boolean connectionOpen;
0188: /**
0189: * list of credentials that can be used for authorization
0190: */
0191: private Vector credentials;
0192: /**
0193: * The request for this client transaction.
0194: */
0195: private Request request = null;
0196: /**
0197: * The initial request saved before ACK is sent.
0198: */
0199: private Request requestSavedBeforeACK = null;
0200: /**
0201: * the response to the actual request
0202: */
0203: private Response response = null;
0204: /**
0205: * The queue of responses for processing.
0206: */
0207: private Vector responses = new Vector();
0208: /**
0209: * the last received response
0210: */
0211: private Response responseReceived = null;
0212: /**
0213: * content of the response body
0214: */
0215: private SDPOutputStream contentOutputStream = null;
0216: /**
0217: * content from the request body
0218: */
0219: private InputStream contentInputStream = null;
0220: /**
0221: * The request URI created from the user, host, port and
0222: * parameters attributes
0223: */
0224: private URI requestURI = null;
0225: /**
0226: * Scheme name
0227: */
0228: private String scheme = null;
0229: /**
0230: * the user part of the SIP URI
0231: */
0232: private String user = null;
0233: /**
0234: * the host part of the SIP URI
0235: */
0236: private String host = null;
0237: /**
0238: * the port Number on which to send the request, part of
0239: * the SIP URI
0240: */
0241: private int port = -1;
0242: /**
0243: * the parameters of the SIP URI
0244: */
0245: private NameValueList parameters = null;
0246: /**
0247: * the sip uri of the user
0248: */
0249: private String userSipURI = "sip:anonymous@anonymous.invalid";
0250: /**
0251: * the client Transaction for an INVITE request
0252: */
0253: private ClientTransaction clientTransaction = null;
0254: /**
0255: * Handle for asynchronous listening thread.
0256: */
0257: private Thread listeningThread = null;
0258: /**
0259: * Current stack of connectors.
0260: */
0261: private StackConnector stackConnector = null;
0262: /**
0263: * Flag of creating internal notifier.
0264: */
0265: private boolean isNotifierCreated = false;
0266: /**
0267: * Flag indicating which SIP message (request or response)
0268: * should be used in getHeader()/getHeaders().
0269: */
0270: private boolean useRequest;
0271: /**
0272: * Permission of generating CANCEL request.
0273: */
0274: protected boolean enableInitCancel = false;
0275: /**
0276: * Count of authorization requests (RFC 2617, 3.2.2).
0277: */
0278: private int countReoriginateRequest = 1;
0279:
0280: /**
0281: * Creates a sip Client Connection to send a request to the
0282: * following SIP URI user@host:portNumber;parameters
0283: * @param inputURI input SIP URI
0284: * @param classSecurityToken Security token for SIP/SIPS protocol class
0285: */
0286: protected SipClientConnectionImpl(SipURI inputURI,
0287: SecurityToken classSecurityToken)
0288: throws IllegalArgumentException {
0289: this .user = inputURI.getUser();
0290: this .host = inputURI.getHost();
0291: this .port = inputURI.getPort();
0292: this .parameters = inputURI.getUriParms();
0293: this .classSecurityToken = classSecurityToken;
0294:
0295: this .scheme = inputURI.getScheme();
0296:
0297: connectionOpen = true;
0298: credentials = new Vector();
0299:
0300: try {
0301: stackConnector = StackConnector
0302: .getInstance(classSecurityToken);
0303: } catch (IOException ioe) {
0304: }
0305:
0306: // Create the REQUEST URI of the request
0307: try {
0308: requestURI = StackConnector.addressFactory
0309: .createURI(scheme + ":"
0310: + ((user == null) ? "" : (user + "@"))
0311: + host);
0312:
0313: if (port != -1) {
0314: ((SipURI) requestURI).setPort(port);
0315: }
0316:
0317: // handle the parameters
0318: if (parameters != null) {
0319: Enumeration parNames = parameters.getKeys();
0320: while (parNames.hasMoreElements()) {
0321: String name = (String) parNames.nextElement();
0322: String value = (String) parameters.getValue(name);
0323: ((SipURI) requestURI).setParameter(name, value);
0324: }
0325: }
0326: } catch (ParseException pe) {
0327: throw new IllegalArgumentException(
0328: "The request URI can not be"
0329: + " created, check the URI syntax");
0330: }
0331:
0332: state = CREATED;
0333: useRequest = true;
0334: }
0335:
0336: /**
0337: * Constructs the client connection implementation.
0338: * @param requestURI the target SIP session URI
0339: * @param sipDialog the current transaction state
0340: */
0341: protected SipClientConnectionImpl(URI requestURI,
0342: SipDialog sipDialog) throws IllegalArgumentException {
0343: if (!requestURI.isSipURI()) {
0344: throw new IllegalArgumentException("URI is not correct");
0345: }
0346:
0347: SipURI sipURI = (SipURI) requestURI;
0348: SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
0349:
0350: user = sipURI.getUser();
0351: host = sipURI.getHost();
0352: port = sipURI.getPort();
0353: parameters = sipURI.getUriParms();
0354: classSecurityToken = sipDialogImpl.getSecurityToken();
0355: scheme = requestURI.getScheme();
0356: connectionOpen = true;
0357: credentials = new Vector();
0358:
0359: // Create the REQUEST URI of the request
0360: this .requestURI = requestURI;
0361: this .sipDialog = sipDialog;
0362: this .refreshID = sipDialogImpl.getRefreshID();
0363:
0364: // this.sipClientConnectionListener =
0365: // ((SipDialogImpl)sipDialog).getSipClientConnectionListener();
0366:
0367: try {
0368: stackConnector = StackConnector
0369: .getInstance(classSecurityToken);
0370: } catch (IOException ioe) {
0371: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
0372: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
0373: "Could not create SipClientConnectionImpl: "
0374: + ioe);
0375: }
0376: }
0377:
0378: sipDialogImpl.dialog.setStack(stackConnector.getSipStack());
0379:
0380: state = CREATED;
0381: useRequest = true;
0382: }
0383:
0384: /**
0385: * Constructs the client connection implementation.
0386: * @param request the current transaction
0387: * @param sipConnectionNotifier the state transition notifier
0388: * @param sipUserURI the user session information
0389: */
0390: private SipClientConnectionImpl(Request request,
0391: SipConnectionNotifier sipConnectionNotifier,
0392: String sipUserURI) throws IllegalArgumentException {
0393: connectionOpen = true;
0394: credentials = new Vector();
0395: // Create the REQUEST of the connection
0396: this .request = request;
0397: this .userSipURI = sipUserURI;
0398: // Create the REQUEST URI of the request
0399: this .requestURI = request.getRequestURI();
0400: this .sipConnectionNotifier = sipConnectionNotifier;
0401:
0402: try {
0403: stackConnector = StackConnector
0404: .getInstance(classSecurityToken);
0405: } catch (IOException ioe) {
0406: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
0407: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
0408: "Could not create SipClientConnectionImpl: "
0409: + ioe);
0410: }
0411: }
0412:
0413: if (request.getMethod().equals(Request.CANCEL)) {
0414: state = INITIALIZED;
0415: } else {
0416: state = CREATED;
0417: }
0418:
0419: useRequest = true;
0420: }
0421:
0422: /**
0423: * Initializes the connection.
0424: * @param method the operation to be performed
0425: * @param scn the state transition notifier
0426: * @exception IllegalArgumentException if the parameters are not
0427: * valid
0428: * @exception SipException if a transition error occurs
0429: * @see SipClientConnection#initRequest
0430: */
0431: public void initRequest(String method, SipConnectionNotifier scn)
0432: throws IllegalArgumentException, SipException {
0433:
0434: if (method == null) {
0435: throw new IllegalArgumentException(
0436: "The method can not be null");
0437: }
0438:
0439: if (state != CREATED) {
0440: throw new SipException(
0441: "the request can not be initialized,"
0442: + " because of wrong state.",
0443: SipException.INVALID_STATE);
0444: }
0445:
0446: if ((state == CREATED)
0447: && ((method.equals(Request.ACK)) || (method
0448: .equals(Request.CANCEL)))) {
0449: throw new SipException(
0450: "the request can not be initialized,"
0451: + " because of wrong state.",
0452: SipException.INVALID_STATE);
0453: }
0454:
0455: if (!Lexer.isValidName(method)) {
0456: throw new IllegalArgumentException("Invalid method: '"
0457: + method + "'");
0458: }
0459:
0460: // Affect the sip connection notifier
0461: if (scn != null) {
0462: sipConnectionNotifier = scn;
0463: } else {
0464: String transport = parameters.getValueDefault(
0465: SIPConstants.GENERAL_TRANSPORT,
0466: SIPConstants.TRANSPORT_UDP);
0467:
0468: int localPort;
0469:
0470: // see RFC 3261, 18.1.1 - selecting port number
0471: if (transport.equalsIgnoreCase(SIPConstants.TRANSPORT_TLS)) {
0472: localPort = SIPConstants.DEFAULT_TLS_PORT;
0473: } else { // other transport protocol
0474: localPort = SIPConstants.DEFAULT_NONTLS_PORT;
0475: }
0476:
0477: // Check if sipConnectionNotifier already exists on the same
0478: // port. This is always true when UAC and UAS are in the same
0479: // application and the user has opened a connection (like
0480: // Connector.open("sip:5060");) before calling this function.
0481: Vector connectionNotifiersList = stackConnector
0482: .getConnectionNotifiersList();
0483:
0484: try {
0485: for (int i = 0; i < connectionNotifiersList.size(); i++) {
0486: SipConnectionNotifier currNotifier = (SipConnectionNotifier) connectionNotifiersList
0487: .elementAt(i);
0488:
0489: if ((currNotifier.getLocalPort() == localPort)
0490: && (((SipConnectionNotifierImpl) currNotifier)
0491: .getSipProvider()
0492: .getListeningPoint().getTransport()
0493: .equalsIgnoreCase(transport))) {
0494: sipConnectionNotifier = currNotifier;
0495: break;
0496: }
0497: }
0498: } catch (IOException ioe) {
0499: throw new SipException(ioe.getMessage(),
0500: SipException.GENERAL_ERROR);
0501: }
0502:
0503: if (sipConnectionNotifier == null) {
0504: // Notifier was not found - create it.
0505: try {
0506: sipConnectionNotifier = stackConnector
0507: .createSipConnectionNotifier(
0508: localPort,
0509: scheme
0510: .equals(SIPConstants.SCHEME_SIP),
0511: transport, null);
0512: } catch (IOException ioe) {
0513: throw new SipException(ioe.getMessage(),
0514: SipException.GENERAL_ERROR);
0515: }
0516:
0517: isNotifierCreated = true;
0518: } // end if
0519: } // end else (scn == null)
0520:
0521: // redirect the methods ACK and CANCEL towards their helper
0522: // methods
0523: if (method.equals(Request.ACK)) {
0524: initAck();
0525: }
0526:
0527: if (method.equals(Request.BYE) && (sipDialog != null)) {
0528: initBye();
0529: state = INITIALIZED;
0530: useRequest = true;
0531: return;
0532: }
0533:
0534: if (method.equals(Request.NOTIFY)) {
0535: // if a dialog was not created, send NOTIFY out of dialog.
0536: if (sipDialog != null) {
0537: initNotify();
0538: state = INITIALIZED;
0539: useRequest = true;
0540: return;
0541: }
0542: }
0543:
0544: // Create request into dialog
0545: if (sipDialog != null) {
0546: byte dialogState = sipDialog.getState();
0547: if ((dialogState == SipDialog.EARLY)
0548: || (dialogState == SipDialog.CONFIRMED)) {
0549:
0550: if (method.equals(Request.PRACK)
0551: && (!((SipDialogImpl) sipDialog).isReliableProvReceived)) {
0552: return;
0553: }
0554:
0555: // if (sipDialog.getState() == SipDialog.CONFIRMED) {
0556: // When SipDialog instance has CONFIRMED state, any new
0557: // request should be inside of dialog and have same
0558: // headers (To, From, Call-ID...) as original request.
0559: try {
0560: request = ((SipDialogImpl) sipDialog).dialog
0561: .createRequest(method);
0562: } catch (SipException ex) {
0563: throw ex;
0564: // throw new IllegalArgumentException(
0565: // "Could not create the bye request! " + ex);
0566: }
0567: state = INITIALIZED;
0568: useRequest = true;
0569: return;
0570: }
0571: }
0572:
0573: // We lookup in a record store to see whether or not there is
0574: // the user sip uri
0575: String sipURI = null;
0576: try {
0577: RecordStore rs = RecordStore.openRecordStore("UserSipUri",
0578: false);
0579: RecordEnumeration re = rs.enumerateRecords(null, null,
0580: false);
0581: if (re.hasNextElement()) {
0582: int recordID = re.nextRecordId();
0583: sipURI = new String(rs.getRecord(recordID));
0584: }
0585: } catch (RecordStoreException rse) {
0586: // rse.printStackTrace();
0587: }
0588:
0589: // if the record store is null the sip uri for the user
0590: // it is an anonymous sip uri
0591: if (sipURI != null) {
0592: userSipURI = sipURI;
0593: }
0594:
0595: Address userAddress = null;
0596: try {
0597: userAddress = StackConnector.addressFactory
0598: .createAddress(userSipURI);
0599: } catch (ParseException pe) {
0600: throw new IllegalArgumentException(
0601: "The system property UserSipUri"
0602: + "can not be parsed, check the syntax");
0603: }
0604:
0605: // Call ID
0606: CallIdHeader callIdHeader = null;
0607: String callId = SIPUtils.generateCallIdentifier(stackConnector
0608: .getSipStack().getIPAddress());
0609: callIdHeader = new CallIdHeader();
0610: callIdHeader.setCallId(callId);
0611:
0612: // CSeq
0613: CSeqHeader cSeqHeader = null;
0614:
0615: try {
0616: cSeqHeader = StackConnector.headerFactory.createCSeqHeader(
0617: 1, method);
0618: } catch (ParseException pe) {
0619: throw new SipException("Problem during the creation"
0620: + " of the CSeqHeader", SipException.GENERAL_ERROR);
0621: }
0622:
0623: // From
0624: FromHeader fromHeader = null;
0625: try {
0626: fromHeader = StackConnector.headerFactory.createFromHeader(
0627: userAddress, StackConnector.generateTag());
0628: } catch (ParseException ex) {
0629: throw new SipException("Problem during the creation"
0630: + " of the FromHeader", SipException.GENERAL_ERROR);
0631: }
0632:
0633: // ToHeader
0634: Address toAddress = StackConnector.addressFactory
0635: .createAddress(requestURI);
0636: ToHeader toHeader = null;
0637: try {
0638: toHeader = StackConnector.headerFactory.createToHeader(
0639: toAddress, null);
0640: } catch (ParseException ex) {
0641: throw new SipException("Problem during the creation"
0642: + " of the ToHeader", SipException.GENERAL_ERROR);
0643: }
0644:
0645: // ViaHeader
0646: Vector viaHeaders = new Vector();
0647: String viaLocalAddress;
0648: String viaTransport;
0649: int viaLocalPort;
0650:
0651: try {
0652: viaLocalAddress = sipConnectionNotifier.getLocalAddress();
0653: viaLocalPort = sipConnectionNotifier.getLocalPort();
0654: viaTransport = ((SipConnectionNotifierImpl) sipConnectionNotifier)
0655: .getSipProvider().getListeningPoint()
0656: .getTransport();
0657: } catch (IOException ioe) {
0658: throw new SipException("Internal Error, cannot get "
0659: + "the local port or address",
0660: SipException.GENERAL_ERROR);
0661: }
0662:
0663: try {
0664: ViaHeader viaHeader = StackConnector.headerFactory
0665: .createViaHeader(viaLocalAddress, viaLocalPort,
0666: viaTransport, SIPUtils.generateBranchId());
0667: viaHeaders.addElement(viaHeader);
0668: } catch (ParseException ex) {
0669: throw new SipException("Problem during the creation"
0670: + " of the ViaHeaders", SipException.GENERAL_ERROR);
0671: }
0672:
0673: // Max Forward Header
0674: MaxForwardsHeader maxForwardsHeader = StackConnector.headerFactory
0675: .createMaxForwardsHeader(70);
0676:
0677: // generate the request
0678: try {
0679: request = StackConnector.messageFactory
0680: .createRequest(requestURI, method, callIdHeader,
0681: cSeqHeader, fromHeader, toHeader,
0682: viaHeaders, maxForwardsHeader);
0683: } catch (ParseException ex) {
0684: throw new SipException("Problem during the creation "
0685: + " of the Request " + method,
0686: SipException.GENERAL_ERROR);
0687: }
0688:
0689: /*
0690: * Contact header - not in MESSAGE request (RFC 3428, 4).
0691: * RFC 3903, p. 5:
0692: * The PUBLISH request MAY contain a Contact header field, but including
0693: * one in a PUBLISH request has no meaning in the event publication
0694: * context and will be ignored by the ESC (Event State Compositor).
0695: */
0696: if (!method.equals(Request.MESSAGE)
0697: && !method.equals(Request.PUBLISH)) {
0698: ContactHeader contactHeader = null;
0699:
0700: try {
0701: if (isNotifierCreated) {
0702: // Notifier was not passed as an argument to initRequest()
0703: SipURI contactURI = StackConnector.addressFactory
0704: .createSipURI("anonymous", // name
0705: viaLocalAddress);
0706: contactURI.setTransportParam(viaTransport);
0707: contactURI.setPort(viaLocalPort);
0708: contactHeader = StackConnector.headerFactory
0709: .createContactHeader(StackConnector.addressFactory
0710: .createAddress(contactURI));
0711: } else { // notifier is given
0712: SipURI contactURI = StackConnector.addressFactory
0713: .createSipURI(userSipURI.substring(scheme
0714: .length() + 1, userSipURI
0715: .indexOf("@")),
0716: sipConnectionNotifier
0717: .getLocalAddress());
0718: contactURI
0719: .setTransportParam(((SipConnectionNotifierImpl) sipConnectionNotifier)
0720: .getSipProvider()
0721: .getListeningPoint().getTransport());
0722: contactHeader = StackConnector.headerFactory
0723: .createContactHeader(StackConnector.addressFactory
0724: .createAddress(contactURI));
0725: contactURI.setPort(sipConnectionNotifier
0726: .getLocalPort());
0727: }
0728: } catch (IOException ioe) {
0729: throw new SipException("Internal Error, cannot get "
0730: + "the local port or address",
0731: SipException.GENERAL_ERROR);
0732: } catch (ParseException ex) {
0733: throw new SipException("Problem during the creation "
0734: + "of the Contact Header",
0735: SipException.GENERAL_ERROR);
0736: }
0737:
0738: // set the header
0739: request.addHeader(contactHeader);
0740: }
0741:
0742: state = INITIALIZED;
0743: useRequest = true;
0744: }
0745:
0746: /**
0747: * @see SipClientConnection#setRequestURI(java.lang.String)
0748: */
0749: /**
0750: * Sets Request-URI explicitly. Request-URI can be set only in
0751: * Initialized state.
0752: * @param newUri Request-URI
0753: * @throws IllegalArgumentException MAY be thrown if the URI is invalid
0754: * @throws SipException INVALID_STATE if the Request-URI can not be set,
0755: * because of wrong state.
0756: * INVALID_OPERATION if the Request-URI is not allowed to be set.
0757: */
0758: public void setRequestURI(String newUri)
0759: throws IllegalArgumentException, SipException {
0760: if (state != INITIALIZED) {
0761: throw new SipException("the request URI can not be set, "
0762: + " because of wrong state.",
0763: SipException.INVALID_STATE);
0764: }
0765:
0766: if (newUri == null) {
0767: throw new IllegalArgumentException("Invalid URI");
0768: }
0769:
0770: URI uri = null;
0771: try {
0772: uri = StackConnector.addressFactory.createURI(newUri);
0773: } catch (ParseException pe) {
0774: throw new IllegalArgumentException("Invalid URI");
0775: }
0776:
0777: request.setRequestURI(uri);
0778: requestURI = uri;
0779: }
0780:
0781: /**
0782: * Convenience method to initialize SipClientConnection with SIP request
0783: * method ACK. ACK can be applied only to INVITE request.
0784: * @see JSR180 spec, v 1.0.1, p 27
0785: *
0786: */
0787: public void initAck() throws SipException {
0788: if (state != COMPLETED) {
0789: throw new SipException(
0790: "the ACK request can not be initialized,"
0791: + " because of wrong state.",
0792: SipException.INVALID_STATE);
0793: }
0794:
0795: // restore first request
0796: if (requestSavedBeforeACK != null) {
0797: request = requestSavedBeforeACK;
0798: }
0799:
0800: if (!request.getMethod().equals(Request.INVITE)) {
0801: // original request is non-INVITE
0802: throw new SipException("Original request is non-INVITE",
0803: SipException.INVALID_OPERATION);
0804: }
0805:
0806: // JSR180: For error responses (3xx-6xx) the ACK is sent
0807: // automatically by the system in transaction level.
0808: // If user initializes an ACK which has already been
0809: // sent an Exception will be thrown.
0810: int statusCode = 0;
0811:
0812: if (response != null) {
0813: statusCode = response.getStatusCode() / 100;
0814: } else if (responseReceived != null) {
0815: statusCode = responseReceived.getStatusCode() / 100;
0816: }
0817:
0818: if (responseReceived.getStatusCode() / 100 > 2) {
0819: throw new SipException("ACK request was already sent",
0820: SipException.INVALID_OPERATION);
0821: }
0822:
0823: requestSavedBeforeACK = request; // save request
0824: // This may throw SipException.
0825: request = clientTransaction.createAck();
0826:
0827: state = INITIALIZED;
0828: useRequest = true;
0829: }
0830:
0831: /**
0832: * Initialize the session termination transaction.
0833: */
0834: protected void initBye() {
0835: // Generate Request
0836: SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
0837: try {
0838: request = sipDialogImpl.dialog.createRequest(Request.BYE);
0839: // handle the parameters
0840: if (parameters != null) {
0841: Enumeration parNames = parameters.getKeys();
0842: while (parNames.hasMoreElements()) {
0843: String name = (String) parNames.nextElement();
0844: String value = (String) parameters.getValue(name);
0845: ((SipURI) requestURI).setParameter(name, value);
0846: }
0847: }
0848: } catch (SipException ex) {
0849: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
0850: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
0851: "Could not create BYE request! " + ex);
0852: }
0853: } catch (ParseException pe) {
0854: // intentionally ignored
0855: // setParameter() is used with verified parameters
0856: }
0857: }
0858:
0859: /**
0860: * Convenience method to initialize SipClientConnection with SIP request
0861: * method NOTIFY.
0862: * This method is copied from latest updates to NIST workspace
0863: */
0864: protected void initNotify() {
0865: // don't call this method out of dialog
0866: if (sipDialog == null) {
0867: throw new IllegalArgumentException(
0868: "Initialization NOTIFY request out of dialog");
0869: }
0870:
0871: // Generate Request
0872: SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
0873: try {
0874: request = sipDialogImpl.dialog
0875: .createRequest(Request.NOTIFY);
0876:
0877: // handle the parameters
0878: if (parameters != null) {
0879: Enumeration parNames = parameters.getKeys();
0880: while (parNames.hasMoreElements()) {
0881: String name = (String) parNames.nextElement();
0882: String value = (String) parameters.getValue(name);
0883: ((SipURI) requestURI).setParameter(name, value);
0884: }
0885: }
0886: } catch (SipException ex) {
0887: // IMPL_NOTE : cleanup
0888: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
0889: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
0890: "Could not create the notify request! " + ex);
0891: }
0892: } catch (ParseException pe) {
0893: // intentionally ignored
0894: // setParameter() is used with verified parameters
0895: }
0896: }
0897:
0898: /**
0899: * Convenience method to initialize SipClientConnection with SIP request
0900: * method CANCEL.
0901: * @return A new SipClientConnection with preinitialized CANCEL request.
0902: * @throws SipException - INVALID_STATE if the request can not be set,
0903: * because of wrong state (in SipClientConnection) or the system
0904: * has already
0905: * got the 200 OK response (even if not read with receive() method).
0906: * INVALID_OPERATION if CANCEL method can not be applied to the current
0907: * request method.
0908: * @see javax.microedition.sip.SipClientConnection#initCancel()
0909: */
0910: public SipClientConnection initCancel() throws SipException {
0911: if ((state != PROCEEDING) || !enableInitCancel) {
0912: throw new SipException(
0913: "the CANCEL request can not be initialized,"
0914: + " because of wrong state.",
0915: SipException.INVALID_STATE);
0916: }
0917:
0918: // JSR180: The CANCEL request will be built according to
0919: // the original INVITE request within this connection.
0920: // Therefore building CANCEL request from not-INVITE
0921: // original request is not allowed.
0922: if (!request.getMethod().equals(Request.INVITE)) {
0923: throw new SipException("The method of original request "
0924: + "is not INVITE", SipException.INVALID_OPERATION);
0925: }
0926:
0927: // init the cancel request
0928: Request cancelRequest = clientTransaction.createCancel();
0929: SipClientConnection sipClientConnectionCancel = new SipClientConnectionImpl(
0930: cancelRequest, sipConnectionNotifier, userSipURI);
0931: // stackConnector.clientConnectionList.addElement(
0932: // sipClientConnectionCancel);
0933: return sipClientConnectionCancel;
0934: }
0935:
0936: /**
0937: * Receives SIP response message. The receive method will update the
0938: * SipClientConnection with the last new received response.
0939: * If no message is received the method will block until something is
0940: * received or specified amount of time has elapsed.
0941: * @param timeout - the maximum time to wait in milliseconds.
0942: * 0 = do not wait, just poll
0943: * @return Returns true if response was received. Returns false if
0944: * the given timeout elapsed and no response was received.
0945: * @throws SipException - INVALID_STATE if the receive can not be
0946: * called because of wrong state.
0947: * @throws IOException - if the message could not be received or
0948: * because of network failure
0949: */
0950: public boolean receive(long timeout) throws SipException,
0951: IOException {
0952: if ((state != PROCEEDING) && (state != COMPLETED)) {
0953: throw new SipException(SipException.INVALID_STATE);
0954: }
0955:
0956: // check for a response
0957: if (responses.isEmpty()) {
0958: // wait for a response during the time specified by the timeout
0959: if (timeout != 0) {
0960: synchronized (this ) {
0961: try {
0962: // listeningThread.sleep(timeout);
0963: wait(timeout);
0964: } catch (InterruptedException ie) {
0965: }
0966: }
0967: }
0968: }
0969:
0970: if (responses.isEmpty()) {
0971: return false; // queue is empty
0972: }
0973:
0974: // get first response from queue
0975: IncomingQueueElement incomingElement = (IncomingQueueElement) responses
0976: .firstElement();
0977: responseReceived = incomingElement.getResponse();
0978: responses.removeElementAt(0); // remove from queue
0979: useRequest = false;
0980:
0981: // change client transaction if need
0982: if (incomingElement.containsClientTransaction()) {
0983: clientTransaction = incomingElement.getClientTransaction();
0984: }
0985:
0986: if ((responseReceived.getStatusCode() / 100 == 2)
0987: && (state == COMPLETED)) { // multiple responses
0988: // change dialog
0989: sipDialog = new SipDialogImpl(
0990: clientTransaction.getDialog(),
0991: sipConnectionNotifier, classSecurityToken);
0992: // transaction is INVITE, checked in
0993: // ClientTransaction.isMessageTransOrMult()
0994: ((SipDialogImpl) sipDialog).setWaitForBye(true);
0995: ((SipDialogImpl) sipDialog).setState(SipDialog.CONFIRMED);
0996: }
0997:
0998: changeDialogState();
0999: changeClientConnectionState();
1000:
1001: return true;
1002: }
1003:
1004: /**
1005: * Sets the listener for incoming responses. If a listener is
1006: * already set it
1007: * will be overwritten. Setting listener to null will remove the current
1008: * listener.
1009: * @param sccl - reference to the listener object. Value null will remove
1010: * the existing listener.
1011: * @throws IOException - if the connection is closed.
1012: */
1013: public void setListener(SipClientConnectionListener sccl)
1014: throws IOException {
1015: if (!connectionOpen) {
1016: throw new IOException("The Connection has been closed!");
1017: }
1018: this .sipClientConnectionListener = sccl;
1019: }
1020:
1021: /**
1022: * Enables the refresh on for the request to be sent. The method return a
1023: * refresh ID, which can be used to update or stop the refresh.
1024: * @param srl - callback interface for refresh events, if this is null the
1025: * method returns 0 and refresh is not enabled.
1026: * @return refresh ID. If the request is not refreshable returns 0.
1027: * @throws SipException - INVALID_STATE if the refresh can not be enabled
1028: * in this state.
1029: */
1030: public int enableRefresh(SipRefreshListener srl)
1031: throws SipException {
1032: if (state != INITIALIZED) {
1033: throw new SipException("can not enable the refresh,"
1034: + " because of wrong state.",
1035: SipException.INVALID_STATE);
1036: }
1037:
1038: if (srl == null) {
1039: return 0;
1040: }
1041:
1042: String method = request.getMethod();
1043: if (!method.equals(Request.REGISTER)
1044: && !method.equals(Request.SUBSCRIBE)
1045: && !method.equals(Request.PUBLISH)) {
1046: return 0;
1047: }
1048:
1049: // understand the refresh listener thing
1050: sipRefreshListener = srl;
1051: int taskID = RefreshManager.getInstance().createRefreshTask(
1052: request, sipConnectionNotifier, sipRefreshListener,
1053: this );
1054: refreshID = String.valueOf(taskID);
1055: return taskID;
1056: }
1057:
1058: /**
1059: * Sets credentials for possible digest authentication.
1060: * The application can set multiple credential triplets
1061: * (username, password, realm) for one SipClientConnection.
1062: * The username and password are specified for certain protection domain,
1063: * which is defined by the realm parameter.
1064: * The credentials can be set:
1065: * before sending the original request in Initialized state.
1066: * The API implementation caches the credentials for later use.
1067: * when 401 (Unauthorized) or 407 (Proxy Authentication Required) response
1068: * is received in the Unauthorized state. The API implementation uses the
1069: * given credentials to re-originate the request with proper authorization
1070: * header. After that the SipClientConnection will be in Proceeding state.
1071: * @param username username (for this protection domain)
1072: * @param password user password (for this protection domain)
1073: * @param realm defines the protection domain
1074: * @throws SipException INVALID_STATE if the credentials can not
1075: * be set in this state.
1076: * @throws NullPointerException - if the username, password or realm is null
1077: */
1078: public void setCredentials(String username, String password,
1079: String realm) throws SipException {
1080: if (state != INITIALIZED && state != UNAUTHORIZED) {
1081: throw new SipException("can not set the credentials, "
1082: + "because of wrong state.",
1083: SipException.INVALID_STATE);
1084: }
1085:
1086: if (username == null || password == null || realm == null) {
1087: throw new NullPointerException();
1088: }
1089:
1090: Credentials credential = new Credentials(username, password,
1091: realm);
1092: credentials.addElement(credential);
1093:
1094: // reoriginate the requests with the proper credentials
1095: if (state == UNAUTHORIZED) {
1096: reoriginateRequest();
1097: }
1098: }
1099:
1100: /**
1101: * Sends the SIP message. Send must also close the OutputStream
1102: * if it was opened.
1103: * @throws IOException if the message could not be sent or because
1104: * of network failure
1105: * @throws InterruptedIOException if a timeout occurs while
1106: * either trying
1107: * to send the message or if this Connection object is closed
1108: * during this send operation
1109: * @throws SipException INVALID_STATE if the message cannot be sent
1110: * in this state. <br> INVALID_MESSAGE there was an error
1111: * in message format
1112: */
1113: public void send() throws IOException, InterruptedIOException,
1114: SipException {
1115: sendRequestImpl(false);
1116: }
1117:
1118: /**
1119: * This function is an implementation for send(). It sends the SIP message.
1120: * Send must also close the OutputStream if it was opened.
1121: * @param isRefreshRequest a flag indicating if the request to be sent
1122: * is a refreshing request (isRefreshRequest is true) or it is a regular
1123: * request (isRefreshRequest is false).
1124: * @throws IOException if the message could not be sent or because
1125: * of network failure
1126: * @throws InterruptedIOException if a timeout occurs while
1127: * either trying
1128: * to send the message or if this Connection object is closed
1129: * during this send operation
1130: * @throws SipException INVALID_STATE if the message cannot be sent
1131: * in this state. <br> INVALID_MESSAGE there was an error
1132: * in message format
1133: */
1134: private void sendRequestImpl(boolean isRefreshRequest)
1135: throws IOException, InterruptedIOException, SipException {
1136:
1137: if (state != STREAM_OPEN && state != INITIALIZED) {
1138: throw new SipException("can not send the request, "
1139: + "because of wrong state.",
1140: SipException.INVALID_STATE);
1141: }
1142:
1143: if (!connectionOpen) {
1144: throw new IOException("The Connection has been closed!");
1145: }
1146:
1147: if (contentOutputStream != null) {
1148: contentOutputStream.setOpen(false);
1149: request.setContent(contentOutputStream
1150: .getByteArrayOutputStream().toByteArray(),
1151: (ContentTypeHeader) request
1152: .getHeader(Header.CONTENT_TYPE));
1153:
1154: contentOutputStream = null;
1155: }
1156:
1157: // Check mandatory headers (RFC3261, 8.1.1)
1158: String[] mandatoryHeaders = { Header.TO, Header.FROM,
1159: Header.CSEQ, Header.CALL_ID, Header.MAX_FORWARDS,
1160: Header.VIA };
1161: Vector mandatoryList = new Vector();
1162:
1163: // add header names for all types of requests
1164: for (int i = 0; i < mandatoryHeaders.length; i++) {
1165: mandatoryList.addElement(mandatoryHeaders[i]);
1166: }
1167:
1168: String method = request.getMethod();
1169:
1170: // RFC 3515, p. 6:
1171: // A REFER request MUST contain exactly one Refer-To header field value.
1172: if (method.equals(Request.REFER)) {
1173: mandatoryList.addElement(Header.REFER_TO);
1174: }
1175:
1176: // RFC3265, p. 15:
1177: // NOTIFY requests MUST contain a "Subscription-State" header with
1178: // a value of "active", "pending", or "terminated".
1179: if (method.equals(Request.NOTIFY)) {
1180: mandatoryList.addElement(Header.SUBSCRIPTION_STATE);
1181: }
1182:
1183: for (int i = 0; i < mandatoryList.size(); i++) {
1184: if (request.getHeader((String) mandatoryList.elementAt(i)) == null) {
1185: throw new SipException("Header "
1186: + (String) mandatoryList.elementAt(i)
1187: + " is missed", SipException.INVALID_STATE);
1188: }
1189: }
1190:
1191: // add "tag" parameter to "From" header if necessary
1192: FromHeader fromHeader = (FromHeader) request
1193: .getHeader(Header.FROM);
1194:
1195: // it is not null - please see above
1196: if (!fromHeader.hasTag()) { // no "tag" parameter
1197: fromHeader.setTag(StackConnector.generateTag());
1198: }
1199:
1200: // Request-URI
1201: // Fix added per NIST cvs digest dated July 3, 2005
1202: // RFC 3261, 10.2:
1203: // Request-URI: ... The "userinfo" and "@" components of the
1204: // SIP URI MUST NOT be present.
1205: if (method.equals(Request.REGISTER)) {
1206: Address reqUriAddress = null;
1207: try {
1208: reqUriAddress = StackConnector.addressFactory
1209: .createAddress(requestURI.toString());
1210: if (reqUriAddress.isSIPAddress()) {
1211: ((SipURI) reqUriAddress.getURI()).removeUser();
1212: requestURI = reqUriAddress.getURI();
1213: }
1214: request.setRequestURI(requestURI);
1215: } catch (ParseException pe) {
1216: throw new SipException(
1217: "The system property UserSipUri can not be "
1218: + "parsed, check the syntax",
1219: SipException.INVALID_OPERATION);
1220: }
1221: }
1222:
1223: // Check that the parameters specified in Via header match
1224: // those which were set in sipConnectionNotifier.
1225: ViaHeader requestViaHeader = (ViaHeader) request
1226: .getViaHeaders().getFirst();
1227: int viaPort = requestViaHeader.getPort();
1228: int localPort = sipConnectionNotifier.getLocalPort();
1229: String transport = requestViaHeader.getTransport();
1230:
1231: if (localPort != viaPort) {
1232: throw new IOException("Via port (" + viaPort + ") doesn't "
1233: + "match the listener's port (" + localPort + ")!");
1234: }
1235:
1236: SipConnectionNotifierImpl notifierImpl = (SipConnectionNotifierImpl) sipConnectionNotifier;
1237:
1238: if (!notifierImpl.getSipProvider().getListeningPoint()
1239: .getTransport().equalsIgnoreCase(transport)) {
1240: throw new IOException("Via transport doesn't match "
1241: + "the listener's transport!");
1242: }
1243:
1244: // RFC 3903 (PUBLISH method), p. 5:
1245: // The Record-Route header field has no meaning in PUBLISH
1246: // requests or responses, and MUST be ignored if present.
1247: if (method.equals(Request.PUBLISH)) {
1248: request.removeHeader(Header.RECORD_ROUTE);
1249: }
1250:
1251: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1252: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
1253: "Request to be sent : " + request);
1254: }
1255:
1256: // System.out.println(">>> Request to be sent: \n" + request);
1257:
1258: // Creates the Nist-Siplite client Transaction for this
1259: // request
1260: try {
1261: clientTransaction = ((SipConnectionNotifierImpl) sipConnectionNotifier)
1262: .getSipProvider().getNewClientTransaction(request);
1263: } catch (TransactionUnavailableException tue) {
1264: throw new SipException("Cannot create a new Client "
1265: + " Transaction for this request",
1266: SipException.TRANSACTION_UNAVAILABLE);
1267: } catch (IllegalArgumentException iae) {
1268: throw new SipException("SCC.send(): IAE occured (1): "
1269: + iae.getMessage(), SipException.GENERAL_ERROR);
1270: } catch (NullPointerException npe) {
1271: throw new SipException("SCC.send(): NPE occured (1): "
1272: + npe.getMessage(), SipException.GENERAL_ERROR);
1273: }
1274:
1275: // Set the application data so that when the response comes in,
1276: // it will retrieve this SipClientConnection
1277: clientTransaction.setApplicationData(this );
1278:
1279: // Send the request
1280:
1281: if (method.equals(Request.ACK)) {
1282: Dialog dlg = clientTransaction.getDialog();
1283: if (dlg.isServer()) {
1284: // Switch from server side to client side for re-invite
1285: dlg.addTransaction(clientTransaction);
1286: }
1287: try {
1288: dlg.sendAck(request);
1289: } catch (IllegalArgumentException iae) {
1290: throw new SipException("SCC.send(): can't send ACK: "
1291: + iae, SipException.GENERAL_ERROR);
1292: }
1293: state = COMPLETED;
1294: return;
1295: } else if (sipDialog != null && !isRefreshRequest) {
1296: // if (method.equals(Request.BYE) ||
1297: // method.equals(Request.NOTIFY)) {
1298: // If the request is a BYE, we must send it with the dialog
1299:
1300: // If the dialog is established, all further requests should
1301: // be sent within it.
1302: SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
1303:
1304: if (method.equals(Request.SUBSCRIBE)
1305: || method.equals(Request.REFER)) {
1306: // Add a subscription
1307: sipDialogImpl.addSubscription(new Subscription(
1308: sipDialogImpl.getDialog(), request));
1309: } else if (method.equals(Request.INVITE)) {
1310: sipDialogImpl.setWaitForBye(true);
1311: }
1312:
1313: sipDialogImpl.dialog.sendRequest(clientTransaction);
1314: state = PROCEEDING;
1315: return;
1316: } else {
1317: clientTransaction.sendRequest();
1318: }
1319:
1320: // An INVITE, SUBSCRIBE or REFER has been sent, so a dialog need to
1321: // be created.
1322: if (stackConnector.getSipStack().isDialogCreated(method)
1323: && !isRefreshRequest) {
1324: sipDialog = new SipDialogImpl(
1325: clientTransaction.getDialog(),
1326: sipConnectionNotifier, classSecurityToken);
1327:
1328: SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
1329: sipDialogImpl.setRefreshID(refreshID);
1330: sipDialogImpl
1331: .setSipClientConnectionListener(sipClientConnectionListener);
1332: stackConnector.sipDialogList.addElement(sipDialog);
1333:
1334: // Add a subscription
1335: if (!method.equals(Request.INVITE)) {
1336: sipDialogImpl.addSubscription(new Subscription(
1337: sipDialogImpl.getDialog(), request));
1338: } else {
1339: sipDialogImpl.setWaitForBye(true);
1340: }
1341: }
1342:
1343: // If the method is a REGISTER it means that we are using a
1344: // proxy so we put put the route of the proxy in the router
1345: if (request.getMethod().equals(Request.REGISTER)) {
1346: SipURI sipURI = (SipURI) request.getRequestURI();
1347:
1348: int requestPort = sipURI.getPort();
1349: if (requestPort == -1) { // get port from sipConnectionNotifier
1350: requestPort = sipConnectionNotifier.getLocalPort();
1351: }
1352:
1353: String requestTransport = sipURI.getTransportParam();
1354:
1355: if ((requestTransport == null)
1356: || (requestTransport.length() < 1)) {
1357: // get transport from sipConnectionNotifier
1358: requestTransport = ((SipConnectionNotifierImpl) sipConnectionNotifier)
1359: .getSipProvider().getListeningPoint()
1360: .getTransport();
1361: }
1362:
1363: stackConnector.sipStack.getRouter().setOutboundProxy(
1364: sipURI.getHost() + ":" + requestPort + "/"
1365: + requestTransport);
1366: // outboundProxy = true;
1367: }
1368:
1369: // Refresh must be scheduled after receiving a response,
1370: // refer the comments at the end of notifyResponseReceived().
1371: // scheduleRefresh(request.getMethod(), request, false);
1372:
1373: state = PROCEEDING;
1374: }
1375:
1376: /**
1377: * Sets header value in SIP message. If the header does not exist
1378: * it will be added to the message, otherwise the existing header is
1379: * overwritten. If multiple header field values exist the topmost is
1380: * overwritten. The implementations MAY restrict the access to some headers
1381: * according to RFC 3261.
1382: * @param name - name of the header, either in full or compact form.
1383: * RFC 3261 p.32
1384: * @param value - the header value
1385: * @throws SipException - INVALID_STATE if header can not be set in
1386: * this state. <br> INVALID_OPERATION if the system does not allow to set
1387: * this header.
1388: * @throws IllegalArgumentException - MAY be thrown if the header or
1389: * value is invalid
1390: */
1391: public void setHeader(String name, String value)
1392: throws SipException, IllegalArgumentException {
1393: if (state != INITIALIZED) {
1394: throw new SipException("the Header can not be set,"
1395: + " because of wrong state.",
1396: SipException.INVALID_STATE);
1397: }
1398:
1399: if (name == null) {
1400: throw new IllegalArgumentException(
1401: "The header name can not be null");
1402: }
1403:
1404: if (value == null) {
1405: throw new IllegalArgumentException(
1406: "The header value can not be null");
1407: }
1408:
1409: Header header = null;
1410:
1411: try {
1412: header = StackConnector.headerFactory.createHeader(name,
1413: value);
1414: // name = header.getName(); // The source name might be expanded
1415: } catch (ParseException pe) {
1416: throw new IllegalArgumentException(pe.getMessage());
1417: }
1418:
1419: if (header == null) {
1420: throw new IllegalArgumentException("null header!");
1421: }
1422:
1423: // Response doesn't exist in the INITIALIZED state, so here
1424: // it's clear which message (request or response) to use.
1425: request.attachHeader(header, true, true);
1426:
1427: /*
1428: if (request.getHeader(name) == null) {
1429: request.addHeader(header);
1430: } else {
1431: request.attachHeader(header, true, true);
1432: }
1433: */
1434: }
1435:
1436: /**
1437: * Adds a header to the SIP message. If multiple header field values exist
1438: * the header value is added topmost of this type of headers.
1439: * The implementations MAY restrict the access to some headers
1440: * according to RFC 3261.
1441: * @param name - name of the header, either in full or compact form.
1442: * RFC 3261 p.32
1443: * @param value - the header value
1444: * @throws SipException - INVALID_STATE if header can not be added in
1445: * this state. <br> INVALID_OPERATION if the system does not allow to add
1446: * this header.
1447: * @throws IllegalArgumentException - MAY be thrown if the header or
1448: * value is invalid
1449: */
1450: public void addHeader(String name, String value)
1451: throws SipException, IllegalArgumentException {
1452: if (state != INITIALIZED) {
1453: throw new SipException("the Header can not be add,"
1454: + " because of wrong state.",
1455: SipException.INVALID_STATE);
1456: }
1457: if (name == null) {
1458: throw new IllegalArgumentException(
1459: "The header name can not be null");
1460: }
1461: if (value == null) {
1462: throw new IllegalArgumentException(
1463: "The header value can not be null");
1464: }
1465: Header header = null;
1466: try {
1467: header = StackConnector.headerFactory.createHeader(name,
1468: value);
1469: } catch (ParseException pe) {
1470: throw new IllegalArgumentException(
1471: "The header can not be created,"
1472: + " check if it is correct");
1473: }
1474: request.addHeader(header);
1475: }
1476:
1477: /**
1478: * Reomves header from the SIP message. If multiple header field
1479: * values exist
1480: * the topmost is removed.
1481: * The implementations MAY restrict the access to some headers
1482: * according to RFC 3261.
1483: * If the named header is not found this method does nothing.
1484: * @param name - name of the header to be removed, either int
1485: * full or compact form RFC 3261 p.32.
1486: * @throws SipException - INVALID_STATE if header can not be removed in
1487: * this state. <br> INVALID_OPERATION if the system does not allow to remove
1488: * this header.
1489: */
1490: public void removeHeader(String name) throws SipException,
1491: IllegalArgumentException {
1492: if (state != INITIALIZED) {
1493: throw new SipException("the Header can not be removed,"
1494: + " because of wrong state.",
1495: SipException.INVALID_STATE);
1496: }
1497: if (name == null) {
1498: throw new IllegalArgumentException(
1499: "The header name can not be null");
1500: }
1501: request.removeHeader(name, true);
1502: }
1503:
1504: /**
1505: * Gets the header field value(s) of specified header type
1506: * @param name - name of the header, either in full or compact form.
1507: * RFC 3261 p.32
1508: * @return array of header field values (topmost first), or null if the
1509: * current message does not have such a header or the header is for other
1510: * reason not available (e.g. message not initialized).
1511: *
1512: * Javadoc is not clear on whether this method should be applied to
1513: * request or response. The NIST implementation uses response only to
1514: * calculate the size; but that seems to be wrong.
1515: *
1516: */
1517: public String[] getHeaders(String name) {
1518: Message currentMessage = useRequest ? (Message) request
1519: : (Message) responseReceived;
1520:
1521: if (currentMessage == null) {
1522: // There 'request' may absent in the CREATED state
1523: return null;
1524: }
1525:
1526: HeaderList nameList = currentMessage.getHeaderList(name);
1527:
1528: if (nameList == null) {
1529: return null;
1530: }
1531:
1532: int size = nameList.size();
1533:
1534: if (size < 1) {
1535: return null;
1536: }
1537:
1538: String[] headerValues = new String[size];
1539:
1540: for (int count = 0; count < size; count++) {
1541: headerValues[count] = ((Header) nameList.elementAt(count))
1542: .getHeaderValue();
1543: }
1544:
1545: return headerValues;
1546: }
1547:
1548: /**
1549: * Gets the header field value of specified header type.
1550: * @param name - name of the header type, either in full or compact form.
1551: * RFC 3261 p.32
1552: * @return topmost header field value, or null if the
1553: * current message does not have such a header or the header is for other
1554: * reason not available (e.g. message not initialized).
1555: *
1556: * Javadoc is not clear on whether this method should be applied to
1557: * request or response. The NIST implementation uses response; but that
1558: * seems to be wrong.
1559: *
1560: */
1561: public String getHeader(String name) {
1562: Message currentMessage = useRequest ? (Message) request
1563: : (Message) responseReceived;
1564:
1565: if (currentMessage == null) {
1566: // There 'request' may absent in the CREATED state
1567: return null;
1568: }
1569:
1570: Header nameHeader = currentMessage.getHeader(name);
1571:
1572: if (nameHeader == null) {
1573: return null;
1574: }
1575:
1576: return nameHeader.getHeaderValue();
1577: }
1578:
1579: /**
1580: * Gets the SIP method. Applicable when a message has been
1581: * initialized or received.
1582: * @return SIP method name REGISTER, INVITE, NOTIFY, etc. Returns null if
1583: * the method is not available.
1584: */
1585: public String getMethod() {
1586: // another implementation returns null in terminated state; so do we
1587: if (null == request || TERMINATED == state) {
1588: return null;
1589: } else {
1590: return request.getMethod();
1591: }
1592: }
1593:
1594: /**
1595: * Gets Request-URI. Available when SipClientConnection is in Initialized
1596: * state or when SipServerConnection is in Request Received state.
1597: * Built from the original URI given in Connector.open().
1598: * See RFC 3261 p.35 (8.1.1.1 Request-URI)
1599: * @return Request-URI of the message. Returns null if the Request-URI
1600: * is not available.
1601: */
1602: public String getRequestURI() {
1603: if (state != INITIALIZED || request == null) {
1604: return null;
1605: } else {
1606: return request.getRequestURI().toString();
1607: }
1608: }
1609:
1610: /**
1611: * Gets SIP response status code. Available when SipClientConnection is in
1612: * Proceeding or Completed state or when SipServerConnection is in
1613: * Initialized state.
1614: * @return status code 1xx, 2xx, 3xx, 4xx, ... Returns 0 if the status code
1615: * is not available.
1616: */
1617: public int getStatusCode() {
1618: if (responseReceived == null) {
1619: return 0;
1620: } else {
1621: return responseReceived.getStatusCode();
1622: }
1623: }
1624:
1625: /**
1626: * Gets SIP response reason phrase. Available when SipClientConnection is in
1627: * Proceeding or Completed state or when SipServerConnection is in
1628: * Initialized state.
1629: * @return reason phrase. Returns null if the reason phrase is
1630: * not available.
1631: */
1632: public String getReasonPhrase() {
1633: if (state != PROCEEDING && state != COMPLETED
1634: && state != UNAUTHORIZED || responseReceived == null) {
1635: return null;
1636: } else {
1637: return responseReceived.getReasonPhrase();
1638: }
1639: }
1640:
1641: /**
1642: * Returns the current SIP dialog. This is available when the SipConnection
1643: * belongs to a created SipDialog and the system has received (or sent)
1644: * provisional (101-199) or final response (200).
1645: * @return SipDialog object if this connection belongs to a dialog,
1646: * otherwise returns null.
1647: */
1648: public SipDialog getDialog() {
1649: if (sipDialog != null) {
1650: byte dialogState = sipDialog.getState();
1651: if ((dialogState != SipDialog.EARLY)
1652: && (dialogState != SipDialog.CONFIRMED)) {
1653: return null;
1654: }
1655: }
1656:
1657: return sipDialog;
1658: }
1659:
1660: /**
1661: * Returns InputStream to read SIP message body content.
1662: * @return InputStream to read body content
1663: * @throws java.io.IOException - if the InputStream can not be opened,
1664: * because of an I/O error occurred.
1665: * @throws SipException - INVALID_STATE the InputStream can not be opened
1666: * in this state (e.g. no message received).
1667: */
1668: public InputStream openContentInputStream() throws IOException,
1669: SipException {
1670: String errStateMsg = "the content input stream can not be open, "
1671: + "because of wrong state.";
1672:
1673: if ((state != COMPLETED) && (state != PROCEEDING)) {
1674: throw new SipException(errStateMsg,
1675: SipException.INVALID_STATE);
1676: }
1677:
1678: if (!connectionOpen) {
1679: throw new IOException("The Connection has been closed!");
1680: }
1681:
1682: if (responseReceived == null) {
1683: // Although openContentInputStream() is called in the correct
1684: // user-level state, we may have a situation when this method
1685: // is called before the response was received. In this case
1686: // the internal state of SCC is invalid and the proper exception
1687: // to throw is SipException.INVALID_STATE.
1688: throw new SipException(errStateMsg,
1689: SipException.INVALID_STATE);
1690: }
1691:
1692: ContentLengthHeader contentLengthHeader = responseReceived
1693: .getContentLengthHeader();
1694: if (contentLengthHeader == null) {
1695: throw new IOException(
1696: "Response contains no content length header.");
1697: }
1698:
1699: int bodyLength = contentLengthHeader.getContentLength();
1700:
1701: if (bodyLength == 0) {
1702: throw new IOException("Response's body has zero length.");
1703: }
1704:
1705: byte[] buf = responseReceived.getRawContent();
1706: if (buf == null) { // body is empty
1707: throw new IOException("Body of SIP response is empty.");
1708: }
1709: contentInputStream = new ByteArrayInputStream(buf);
1710: return contentInputStream;
1711: }
1712:
1713: /**
1714: * Returns OutputStream to fill the SIP message body content.
1715: * When calling close() on OutputStream the message will be sent
1716: * to the network. So it is equivalent to call send(). Again send() must
1717: * not be called after closing the OutputStream, since it will throw
1718: * Exception because of calling the method in wrong state.
1719: * Before opening OutputStream the Content-Length and Content-Type headers
1720: * has to se set. If not SipException.UNKNOWN_LENGTH or
1721: * SipException.UNKNOWN_TYPE will be thrown respectively.
1722: * @return OutputStream to write body content
1723: * @throws IOException if the OutputStream can not be opened,
1724: * because of an I/O error occurred.
1725: * @throws SipException INVALID_STATE the OutputStream can not be opened
1726: * in this state (e.g. no message initialized).
1727: * UNKNOWN_LENGTH Content-Length header not set.
1728: * UNKNOWN_TYPE Content-Type header not set.
1729: */
1730: public OutputStream openContentOutputStream() throws IOException,
1731: SipException {
1732: if (state != INITIALIZED) {
1733: throw new SipException(
1734: "the content output stream can not be open,"
1735: + " because of wrong state.",
1736: SipException.INVALID_STATE);
1737: }
1738: if (request.getHeader(Header.CONTENT_TYPE) == null) {
1739: throw new SipException(
1740: "Content-Type unknown, set the content-type "
1741: + "header first", SipException.UNKNOWN_TYPE);
1742: }
1743: if (request.getHeader(Header.CONTENT_LENGTH) == null) {
1744: throw new SipException("Content-Length unknown, set the "
1745: + "content-length header first",
1746: SipException.UNKNOWN_LENGTH);
1747: }
1748: if (!connectionOpen) {
1749: throw new IOException("The Connection has been closed!");
1750: }
1751: contentOutputStream = new SDPOutputStream(this );
1752: state = STREAM_OPEN;
1753: return contentOutputStream;
1754: }
1755:
1756: /**
1757: * Close the clientconnection.
1758: * @see javax.microedition.io.Connection#close()
1759: */
1760: public void close() throws IOException {
1761: responses.removeAllElements(); // clear queue
1762: // cleanup
1763: if (isNotifierCreated && (sipConnectionNotifier != null)) {
1764: // try {
1765: sipConnectionNotifier.close();
1766: // } catch (IOException exc) { // ignore
1767: // }
1768: }
1769:
1770: // Removing the connection from the connection list held by
1771: // the stackConnector
1772:
1773: // StackConnector.getInstance().
1774: // clientConnectionList.removeElement(this);
1775: connectionOpen = false;
1776: listeningThread = null;
1777: state = TERMINATED;
1778: }
1779:
1780: /**
1781: * Reoriginate the request with the proper credentials
1782: */
1783: private void reoriginateRequest() {
1784:
1785: // clear dialog
1786: if (sipDialog != null) {
1787: if (sipDialog.getState() == SipDialog.TERMINATED) {
1788: sipDialog = null;
1789: }
1790: }
1791:
1792: DigestClientAuthentication authentication = new DigestClientAuthentication(
1793: credentials);
1794:
1795: // Reoriginate the request with the proper credentials
1796: Request newRequest = authentication.createNewRequest(
1797: stackConnector.sipStack, this .request,
1798: this .responseReceived, this .countReoriginateRequest);
1799:
1800: if (newRequest != null) {
1801: this .countReoriginateRequest++;
1802: this .request = newRequest;
1803:
1804: // The request has been reinitialized...
1805: state = INITIALIZED;
1806: useRequest = true;
1807:
1808: // ...so it is sent out
1809: try {
1810: this .send();
1811: } catch (IOException ioe) {
1812: // ioe.printStackTrace();
1813: }
1814: }
1815: }
1816:
1817: /**
1818: * Change the state of this Client Connection due to an incoming response.
1819: */
1820: private void changeClientConnectionState() {
1821: // Change the Client Connection state
1822: // If it's a trying, the state is PROCEEDING
1823: if (responseReceived.getStatusCode() / 100 == 1
1824: && state == PROCEEDING) {
1825: state = PROCEEDING;
1826: }
1827: // If it's a 401 or 407, the state is UNAUTHORIZED
1828: else if (state == PROCEEDING
1829: && (responseReceived.getStatusCode() == Response.UNAUTHORIZED || responseReceived
1830: .getStatusCode() == Response.PROXY_AUTHENTICATION_REQUIRED)) {
1831: state = UNAUTHORIZED;
1832: }
1833: // Otherwise this is COMPLETED
1834: else {
1835: state = COMPLETED;
1836: }
1837: }
1838:
1839: /**
1840: * Change the state of the dialog due to an incoming response.
1841: */
1842: private void changeDialogState() {
1843: // Change the dialog state
1844:
1845: // REGISTER method doesn't establish a dialog, so sipDialog
1846: // should be null in this case.
1847: // IMPL_NOTE: check if it is really null as supposed to be.
1848: if (sipDialog == null) {
1849: return;
1850: }
1851:
1852: SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
1853: String method = responseReceived.getCSeqHeader().getMethod();
1854: if (!stackConnector.getSipStack()
1855: .allowDialogStateChange(method)) {
1856: return;
1857: }
1858: int statusCode = responseReceived.getStatusCode();
1859:
1860: if (statusCode / 100 == 2) {
1861: // RFC 3261, section 13.2.2.4:
1862: // If the dialog identifier in the 2xx response matches the dialog
1863: // identifier of an existing dialog, the dialog MUST be transitioned
1864: // to the "confirmed" state.
1865:
1866: // sipDialog cannot be null here - this check was done
1867: // at the beginning of the function
1868: sipDialogImpl.setState(SipDialog.CONFIRMED);
1869: sipDialogImpl.setDialogID(responseReceived
1870: .getDialogId(false));
1871:
1872: if (statusCode == 200) {
1873: if (method.equals(Request.NOTIFY)) {
1874: sipDialogImpl.handleNotify(request, null,
1875: responseReceived.getDialogId(false));
1876: return;
1877: }
1878:
1879: // handle the un-Subscribe state
1880: if (method.equals(Request.SUBSCRIBE)) {
1881: ExpiresHeader expiresHeader = (ExpiresHeader) responseReceived
1882: .getHeader(ExpiresHeader.NAME);
1883:
1884: // According to RFC3265, p. 6:
1885: // "200-class responses to SUBSCRIBE requests also
1886: // MUST contain an "Expires" header."
1887: //
1888: // But we have to handle a situation when it is missing.
1889: // RFC 3265, p. 8:
1890: // "200-class responses indicate that the subscription
1891: // has been accepted".
1892: //
1893: // So, the dialog state is set to CONFIRMED even if
1894: // no Expires header is present.
1895: if (expiresHeader != null
1896: && expiresHeader.getExpires() == 0) { // unsubscribe
1897: sipDialogImpl.terminateIfNoSubscriptions();
1898: } else { // subscribe confirmation
1899: sipDialogImpl.setState(SipDialog.CONFIRMED);
1900: sipDialogImpl.setDialogID(responseReceived
1901: .getDialogId(false));
1902: }
1903: } else if (method.equals(Request.BYE)) {
1904: // IMPL_NOTE: check the RFC 3261. Probably we have to terminate
1905: // the dialog for the responses other than 200 OK.
1906: sipDialogImpl.setWaitForBye(false);
1907: sipDialogImpl.terminateIfNoSubscriptions();
1908: }
1909: }
1910: } else if (statusCode / 100 == 1) {
1911: // provisional response
1912: if (statusCode != 100) {
1913: if (sipDialog.getState() == SipDialogImpl.INITIALIZED) {
1914: // switch to EARLY state
1915: sipDialogImpl.setState(SipDialog.EARLY);
1916: }
1917: /*
1918: * Add a check to verify if it is reliable provisional
1919: * response.
1920: */
1921: Header requireHeader = (ParameterLessHeader) responseReceived
1922: .getHeader(Header.REQUIRE);
1923: if (requireHeader != null) {
1924: sipDialogImpl.isReliableProvReceived = Header
1925: .isReliableTagPresent(requireHeader
1926: .getHeaderValue());
1927: }
1928: // RFC 3261, 12.1:
1929: // Within this specification, only 2xx and 101-199
1930: // responses with a To tag ... will establish a dialog.
1931: // set the dialog ID
1932: sipDialogImpl.setDialogID(responseReceived
1933: .getDialogId(false));
1934: }
1935:
1936: } else { // another response code - switch to TERMINATED state
1937: // Remove the subscription if any
1938: sipDialogImpl.removeSubscription(response);
1939:
1940: // JSR180 - not from CONFIRMED state
1941: if (sipDialog.getState() != SipDialog.CONFIRMED) {
1942: if (method.equals(Request.INVITE)) {
1943: sipDialogImpl.setWaitForBye(false);
1944: }
1945: sipDialogImpl.terminateIfNoSubscriptions();
1946: }
1947: }
1948: }
1949:
1950: /**
1951: * Updates and sends the request from the refresh.
1952: * @param updatedRequest the updated request
1953: * @throws IOException if the message could not be sent or because
1954: * of network failure
1955: * @throws InterruptedIOException if a timeout occurs while
1956: * either trying
1957: * to send the message or if this Connection object is closed
1958: * during this send operation
1959: * @throws SipException INVALID_STATE if the message cannot be sent
1960: * in this state. <br> INVALID_MESSAGE there was an error
1961: * in message format
1962: */
1963: protected void updateAndSendRequestFromRefresh(
1964: Request updatedRequest) throws IOException,
1965: InterruptedIOException, SipException {
1966: request = updatedRequest;
1967: state = INITIALIZED;
1968:
1969: // If the request to be refreshed creates a dialog (i.e. SUBSCRIBE),
1970: // the next request will be sent within a dialog using the rules for
1971: // sending in-dialog requests. To avoid it, isRefreshRequest parameter
1972: // is used.
1973: sendRequestImpl(true);
1974: }
1975:
1976: /**
1977: * Updates the request and calls openContentOutputStream()
1978: * to fill the new message body.
1979: * @param updatedRequest the updated request
1980: * @return OutputStream to write body content
1981: * @throws IOException if the OutputStream can not be opened,
1982: * because of an I/O error occurred.
1983: * @throws SipException INVALID_STATE the OutputStream can not be opened
1984: * in this state (e.g. no message initialized).
1985: * UNKNOWN_LENGTH Content-Length header not set.
1986: * UNKNOWN_TYPE Content-Type header not set.
1987: */
1988: protected OutputStream updateRequestAndOpenOutputStream(
1989: Request updatedRequest) throws IOException, SipException {
1990: request = updatedRequest;
1991: state = INITIALIZED;
1992: return openContentOutputStream();
1993: }
1994:
1995: /**
1996: * Gets the current request.
1997: * @return the current request
1998: */
1999: public Request getRequest() {
2000: return request;
2001: }
2002:
2003: /**
2004: * Return the Call Identifier of this client connection
2005: * If there is no call id yet, this method return an empty String
2006: * @return the call Identifier
2007: */
2008: protected String getCallIdentifier() {
2009: if (request == null) {
2010: return "";
2011: }
2012:
2013: return request.getCallIdentifier();
2014: }
2015:
2016: /**
2017: * The stack connector notifies this class when it receive NOTIFY
2018: * request matching the previous SUBSCRIBE or REFER request.
2019: * @param notifyRequest NOTIFY request to process
2020: */
2021: public void handleMatchingNotify(Request notifyRequest) {
2022: SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
2023: int state = sipDialogImpl.getState();
2024: SubscriptionStateHeader ssh = (SubscriptionStateHeader) notifyRequest
2025: .getHeader(Header.SUBSCRIPTION_STATE);
2026: boolean isUnsubscribe = (ssh != null && ssh.isTerminated());
2027:
2028: if (state == SipDialogImpl.INITIALIZED
2029: || (state == SipDialogImpl.CONFIRMED && isUnsubscribe)) {
2030: String dialogId = notifyRequest.getDialogId(false);
2031: // sipDialogImpl.setState(isUnsubscribe ?
2032: // SipDialog.TERMINATED : SipDialog.CONFIRMED);
2033: sipDialogImpl.setDialogID(dialogId);
2034: sipDialogImpl.handleNotify(notifyRequest, null, dialogId);
2035: }
2036: }
2037:
2038: /**
2039: * The stack connector notifies this class when it receive a new response.
2040: * @param response the repsonse event to be propagated
2041: * @param inputClientTransaction client transaction of this response
2042: */
2043: protected void notifyResponseReceived(Response response,
2044: ClientTransaction inputClientTransaction) {
2045: // System.out.println(">>> Response received : \n" + response);
2046: int statusCode = response.getStatusCode();
2047: int statusGroup = statusCode / 100;
2048:
2049: boolean ignoreResponse = false;
2050:
2051: if (state == COMPLETED) { // 2xx responses only
2052: if (statusGroup != 2) {
2053: ignoreResponse = true;
2054: }
2055: } else if (state == PROCEEDING) {
2056: // If there is some credentials and the client connection is in an
2057: // UNAUTHORIZED state, the request is reoriginate automatically
2058: if (credentials.size() > 0
2059: && ((statusCode == Response.UNAUTHORIZED) || (statusCode == Response.PROXY_AUTHENTICATION_REQUIRED))) {
2060: this .responseReceived = response;
2061: if (sipDialog != null) {
2062: ((SipDialogImpl) sipDialog)
2063: .setState(SipDialog.TERMINATED);
2064: }
2065: reoriginateRequest();
2066: ignoreResponse = true;
2067: }
2068: } else { // not COMPLETED and PROCEEDING
2069: ignoreResponse = true;
2070: }
2071:
2072: // check the queue size
2073: if (responses.size() > MAX_NUM_RESPONSES) {
2074: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
2075: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
2076: "Queue of incoming SIP packets is overflow");
2077: }
2078: ignoreResponse = true;
2079: }
2080:
2081: if (ignoreResponse) { // ignore response
2082: return;
2083: }
2084:
2085: IncomingQueueElement incomingElement = new IncomingQueueElement(
2086: response, inputClientTransaction);
2087: responses.addElement(incomingElement); // put to queue
2088:
2089: this .response = response;
2090:
2091: if (state == PROCEEDING) {
2092: if (statusGroup == 1) {
2093: // provisional response
2094: // JSR180: SipClientConnection: initCancel: The method is
2095: // available when a provisional response
2096: // has been received.
2097: enableInitCancel = true;
2098: } else {
2099: // All responses from 200-699 are final
2100: // JSR180: SipClientConnection: initCancel:
2101: // Throws: SipException - INVALID_STATE if ... or the system
2102: // has already got the 200 OK response (even if not read with
2103: // receive() method).
2104: enableInitCancel = false;
2105: }
2106: }
2107:
2108: if (response.getCSeqHeaderNumber() == request
2109: .getCSeqHeaderNumber()) {
2110: synchronized (this ) {
2111: notify();
2112: }
2113: // We notify the listener that a response has been received
2114: if (sipClientConnectionListener != null) {
2115: sipClientConnectionListener.notifyResponse(this );
2116: }
2117: }
2118:
2119: String method = response.getCSeqHeader().getMethod();
2120:
2121: if (method.equals(Request.PUBLISH)) {
2122: // RFC 3903, p. 6:
2123: // When updating previously published event state, PUBLISH
2124: // requests MUST contain a single SIP-If-Match header field
2125: // identifying the specific event state that the request is
2126: // refreshing, modifying or removing. This header field MUST
2127: // contain a single entity-tag that was returned by the ESC
2128: // in the SIP-ETag header field of the response to a previous
2129: // publication.
2130: Header hEtag = response.getHeader(Header.SIP_ETAG);
2131:
2132: if (hEtag != null) {
2133: Header hIfMatch = request
2134: .getHeader(Header.SIP_IF_MATCH);
2135:
2136: if (hIfMatch == null) {
2137: Exception ex = null;
2138: // Create SIP_IF_MATCH header
2139: try {
2140: hIfMatch = StackConnector.headerFactory
2141: .createHeader(Header.SIP_IF_MATCH,
2142: hEtag.getHeaderValue());
2143: request.addHeader(hIfMatch);
2144: } catch (NullPointerException npe) {
2145: ex = npe;
2146: } catch (ParseException pe) {
2147: ex = pe;
2148: } catch (SipException se) {
2149: ex = se;
2150: }
2151: if (ex != null) {
2152: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
2153: Logging.report(Logging.ERROR,
2154: LogChannels.LC_JSR180,
2155: "scc.notifyResponseReceived(): can't create "
2156: + "SIP-If-Match header:"
2157: + ex);
2158: ex.printStackTrace();
2159: }
2160: }
2161: } else {
2162: hIfMatch.setHeaderValue(hEtag.getHeaderValue());
2163: }
2164:
2165: request.removeHeader(Header.SIP_ETAG);
2166: } else {
2167: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
2168: Logging
2169: .report(
2170: Logging.WARNING,
2171: LogChannels.LC_JSR180,
2172: "scc.notifyResponseReceived(): response to PUBLISH "
2173: + "doesn't contain SIP-Etag header!");
2174: }
2175: }
2176: }
2177:
2178: // Schedule the refresh if required (i.e., if the method is
2179: // refreshable). Refresh time is taken from the response,
2180: // either from Expires header or from "expires" parameter
2181: // of Contact header as described in RFCs 3261, section 10.2.4
2182: // and RFC 3265, section 3.1.1.
2183: // If a listener for refresh event has been set, it is notified.
2184: if (statusCode == Response.OK) {
2185: scheduleRefresh(method);
2186: }
2187:
2188: // Notify the listener
2189: if (refreshID != null) {
2190: sipRefreshListener.refreshEvent(
2191: Integer.parseInt(refreshID), statusCode, response
2192: .getReasonPhrase());
2193: }
2194: }
2195:
2196: /**
2197: * Gets the current state of connection.
2198: * @return the current state
2199: */
2200: public int getState() {
2201: return state;
2202: }
2203:
2204: /**
2205: * Gets the current client transaction.
2206: * @return the current client transaction
2207: */
2208: protected ClientTransaction getClientTransaction() {
2209: return clientTransaction;
2210: }
2211:
2212: /**
2213: * Gets the current sip stack.
2214: * @return the current sip stack
2215: */
2216: protected SipStack getSipStack() {
2217: return stackConnector.getSipStack();
2218: }
2219:
2220: /**
2221: * Gets the assigned SipConnectionNotifier.
2222: * @return the current SipConnectionNotifier
2223: */
2224: protected SipConnectionNotifier getSipConnectionNotifier() {
2225: return sipConnectionNotifier;
2226: }
2227:
2228: /**
2229: * Gets the response.
2230: * @return the response instance
2231: */
2232: protected Response getResponse() {
2233: return response;
2234: }
2235:
2236: /**
2237: * Clears the current response.
2238: */
2239: protected void clearResponse() {
2240: this .response = null;
2241: }
2242:
2243: /**
2244: * Schedules refreshing of the request if required.
2245: * @param method SIP method of the message
2246: */
2247: private void scheduleRefresh(String method) {
2248: if (sipRefreshListener == null) {
2249: return;
2250: }
2251:
2252: if (!(method.equals(Request.REGISTER)
2253: || method.equals(Request.SUBSCRIBE) || method
2254: .equals(Request.PUBLISH))) {
2255: return;
2256: }
2257:
2258: // Remove the body of the message in case if the request is PUBLISH,
2259: // see RFC 3903, p. 7 (section 4.1):
2260: // +-----------+-------+---------------+---------------+
2261: // | Operation | Body? | SIP-If-Match? | Expires Value |
2262: // +-----------+-------+---------------+---------------+
2263: // | Initial | yes | no | > 0 |
2264: // | Refresh | no | yes | > 0 |
2265: // | Modify | yes | yes | > 0 |
2266: // | Remove | no | yes | 0 |
2267: // +-----------+-------+---------------+---------------+
2268: if (method.equals(Request.PUBLISH)) {
2269: request.removeContent();
2270: }
2271:
2272: // If the expires is set, the refresh is scheduled for the
2273: // duration of the expires
2274: int expires, minExpires = Integer.MAX_VALUE;
2275:
2276: // RFC 3265, p. 6:
2277: // An "expires" parameter on the "Contact" header has no semantics for
2278: // SUBSCRIBE and is explicitly not equivalent to an "Expires" header in
2279: // a SUBSCRIBE request or response.
2280: if (!method.equals(Request.SUBSCRIBE)) {
2281: ContactList cl = response.getContactHeaders();
2282:
2283: if (cl != null) {
2284: // Take a minimal expiration time from Contact headers.
2285: Enumeration en = cl.getElements();
2286:
2287: while (en.hasMoreElements()) {
2288: ContactHeader contactHeader = (ContactHeader) en
2289: .nextElement();
2290:
2291: if (contactHeader != null) {
2292: try {
2293: expires = Integer.parseInt(contactHeader
2294: .getExpires());
2295: if ((expires > 0) && (expires < minExpires)) {
2296: minExpires = expires;
2297: }
2298: } catch (NumberFormatException e) {
2299: // intentionally ignored
2300: // in the worst case
2301: // minExpires = Integer.MAX_VALUE
2302: }
2303: }
2304: }
2305: }
2306: } // end if (not SUBSCRIBE)
2307:
2308: // Take an expiration time from the Expires header.
2309: ExpiresHeader expiresHeader = (ExpiresHeader) response
2310: .getHeader(ExpiresHeader.NAME);
2311:
2312: if (expiresHeader != null) {
2313: expires = expiresHeader.getExpires();
2314:
2315: if ((expires > 0) && (expires < minExpires)) {
2316: minExpires = expires;
2317: }
2318: }
2319:
2320: if (minExpires == Integer.MAX_VALUE || minExpires < 0) {
2321: // Apply defaults.
2322: minExpires = 3600;
2323: }
2324:
2325: // System.out.println(">>> Refresh time: " + minExpires);
2326:
2327: /*
2328: if (expiresHeader != null) {
2329: expires = expiresHeader.getExpires();
2330: System.out.println(">>> From header: " + expires);
2331: }
2332: */
2333:
2334: if (minExpires != 0) {
2335: RefreshManager.getInstance().scheduleTask(refreshID,
2336: minExpires);
2337: }
2338: }
2339: }
|