Source Code Cross Referenced for SipClientConnectionImpl.java in  » 6.0-JDK-Modules » j2me » gov » nist » microedition » sip » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » j2me » gov.nist.microedition.sip 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.