/** * $RCSfile: ,v $ * $Revision: $ * $Date: $ * * Copyright (C) 2004-2011 Jive Software. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sipmack.sip; import java.net.InetAddress; import java.net.InetSocketAddress; import java.text.ParseException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.TooManyListenersException; import javax.sdp.SessionDescription; import javax.sip.ClientTransaction; import javax.sip.Dialog; import javax.sip.DialogTerminatedEvent; import javax.sip.IOExceptionEvent; import javax.sip.InvalidArgumentException; import javax.sip.ListeningPoint; import javax.sip.ObjectInUseException; import javax.sip.PeerUnavailableException; import javax.sip.RequestEvent; import javax.sip.ResponseEvent; import javax.sip.ServerTransaction; import javax.sip.SipException; import javax.sip.SipFactory; import javax.sip.SipListener; import javax.sip.SipProvider; import javax.sip.SipStack; import javax.sip.TimeoutEvent; import javax.sip.Transaction; import javax.sip.TransactionAlreadyExistsException; import javax.sip.TransactionTerminatedEvent; import javax.sip.TransactionUnavailableException; import javax.sip.TransportNotSupportedException; import javax.sip.address.Address; import javax.sip.address.AddressFactory; import javax.sip.address.SipURI; import javax.sip.header.CSeqHeader; import javax.sip.header.ContactHeader; import javax.sip.header.FromHeader; import javax.sip.header.HeaderFactory; import javax.sip.header.MaxForwardsHeader; import javax.sip.header.ToHeader; import javax.sip.header.ViaHeader; import javax.sip.message.Message; import javax.sip.message.MessageFactory; import javax.sip.message.Request; import javax.sip.message.Response; import net.java.sipmack.common.Log; import net.java.sipmack.sip.event.CallEvent; import net.java.sipmack.sip.event.CallRejectedEvent; import net.java.sipmack.sip.event.CommunicationsErrorEvent; import net.java.sipmack.sip.event.CommunicationsListener; import net.java.sipmack.sip.event.MessageEvent; import net.java.sipmack.sip.event.RegistrationEvent; import net.java.sipmack.sip.event.UnknownMessageEvent; import net.java.sipmack.sip.security.Credentials; import net.java.sipmack.sip.security.SecurityAuthority; import net.java.sipmack.sip.security.SipSecurityManager; import net.java.sipmack.sip.simple.MessageProcessing; /** * Title: SIPark * Description: JAIN-SIP Audio/Video phone application * * @author Thiago Rocha Camargo (thiago@jivesoftware.com) The * <code>SipManager</code> class that Manage SIP Stack * @version 1.0, 20/07/2006 */ public class SipManager implements SipListener { /** * Specifies the number of retries that should be attempted when deleting a * sipProvider */ protected static final int RETRY_OBJECT_DELETES = 10; protected static final long REGISTER_TIMEOUT = 20000; /** * Specifies the time to wait before retrying delete of a sipProvider. */ protected static final long RETRY_OBJECT_DELETES_AFTER = 500; protected static final String DEFAULT_TRANSPORT = "udp"; // jain-sip objects - package accessibility as they should be // available for XxxProcessing classes /** * The SipFactory instance used to create the SipStack and the Address * Message and Header Factories. */ public SipFactory sipFactory; /** * The AddressFactory used to create URLs ans Address objects. */ public AddressFactory addressFactory; /** * The HeaderFactory used to create SIP message headers. */ public HeaderFactory headerFactory; /** * The Message Factory used to create SIP messages. */ public MessageFactory messageFactory; private int loginfailed = 0; /** * The sipStack instance that handles SIP communications. */ SipStack sipStack; /** * The busy state of this SIP Manager */ public boolean isBusy = false; /** * The default (and currently the only) SIP listening point of the * application. */ ListeningPoint listeningPoint; /** * The JAIN SIP SipProvider instance. */ public SipProvider sipProvider; /** * An instance used to provide user credentials */ private SecurityAuthority securityAuthority = null; /** * Used for the contact header to provide firewall support. */ private InetSocketAddress publicIpAddress = null; // properties protected String sipStackPath = "gov.nist"; /** */ protected String currentlyUsedURI = null; protected String username = null; protected String displayName = null; protected String transport = null; protected String registrarAddress = null; protected int localPort = -1; protected int registrarPort = -1; protected int registrationsExpiration = -1; protected String registrarTransport = null; private int registerRetries = 0; // mandatory stack properties protected String stackAddress = null; protected String stackName = "SIPark"; // Prebuilt Message headers /** */ protected FromHeader fromHeader = null; /** */ protected ContactHeader contactHeader = null; protected ArrayList<ViaHeader> viaHeaders = null; protected static final int MAX_FORWARDS = 70; /** */ protected MaxForwardsHeader maxForwardsHeader = null; protected long registrationTransaction = -1; protected List<CommunicationsListener> listeners = new ArrayList<CommunicationsListener>(); // Processing managers /** * The instance that handles all registration associated activity such as * registering, unregistering and keeping track of expirations. */ RegisterProcessing registerProcessing = null; /** * The instance that handles all call associated activity such as * establishing, managing, and terminating calls. */ CallProcessing callProcessing = null; /** * The instance that handles incoming/outgoing MESSAGE requests. */ public MessageProcessing messageProcessing = null; /** * The instance that handles incoming/outgoing REFER requests. */ TransferProcessing transferProcessing = null; /** * Authentication manager. */ public SipSecurityManager sipSecurityManager = null; /** */ protected boolean isStarted = false; /** * Constructor. It only creates a SipManager instance without initializing * the stack itself. */ public SipManager() { registerProcessing = new RegisterProcessing(this); callProcessing = new CallProcessing(this); messageProcessing = new MessageProcessing(this); transferProcessing = new TransferProcessing(this, callProcessing); sipSecurityManager = new SipSecurityManager(); registerRetries = 0; } /** * Creates and initializes JAIN SIP objects (factories, stack, listening * point and provider). Once this method is called the application is ready * to handle (incoming and outgoing) sip messages. * * @throws CommunicationsException if an axception should occur during the initialization * process */ public void start() throws CommunicationsException { try { initProperties(); SIPConfig.setSystemProperties(); this.sipFactory = SipFactory.getInstance(); sipFactory.setPathName(sipStackPath); try { addressFactory = sipFactory.createAddressFactory(); headerFactory = sipFactory.createHeaderFactory(); messageFactory = sipFactory.createMessageFactory(); } catch (PeerUnavailableException ex) { Log.error("start", ex); throw new CommunicationsException( "Could not create factories!", ex); } try { sipStack = sipFactory.createSipStack(System.getProperties()); ((SipCommRouter) sipStack.getRouter()) .setOutboundProxy(SIPConfig.getOutboundProxy()); } catch (PeerUnavailableException ex) { Log.error("start", ex); throw new CommunicationsException( "Cannot connect!\n" + "Please verify your connection.", ex); } try { boolean successfullyBound = false; int tries = 0; while (!successfullyBound) { try { // try and capture the firewall mapping for this address // just befre it gets occuppied by the stack publicIpAddress = NetworkAddressManager .getPublicAddressFor(localPort); listeningPoint = sipStack.createListeningPoint(publicIpAddress.getAddress().getHostAddress(), localPort, transport); } catch (InvalidArgumentException ex) { Log.error("start", ex); // choose another port between 1024 and 65000 if (tries > 3) { if (!NetworkAddressManager.nextIndex()) throw new CommunicationsException( "Cannot connect!\n" + "Please verify your connection.", ex); tries = 0; } tries++; localPort = (int) ((65000 - 1024) * Math.random()) + 1024; try { Thread.sleep(1000); } catch (Exception e) { } continue; } successfullyBound = true; } } catch (TransportNotSupportedException ex) { throw new CommunicationsException( "Transport " + transport + " is not suppported by the stack!\n Try specifying another" + " transport in Mais property files.\n", ex); } try { sipProvider = sipStack.createSipProvider(listeningPoint); } catch (ObjectInUseException ex) { Log.error("start", ex); throw new CommunicationsException( "Could not create factories!\n", ex); } try { sipProvider.addSipListener(this); } catch (TooManyListenersException exc) { throw new CommunicationsException( "Could not register SipManager as a sip listener!", exc); } // we should have a security authority to be able to handle // authentication if (sipSecurityManager.getSecurityAuthority() == null) { throw new CommunicationsException( "No SecurityAuthority was provided to SipManager!"); } // we should have also have a SubsciptionAuthority to be able to // handle // incoming Subscription requests. if (sipSecurityManager.getSecurityAuthority() == null) { throw new CommunicationsException( "No SubscriptionAuthority was provided to SipManager!"); } sipSecurityManager.setHeaderFactory(headerFactory); sipSecurityManager.setTransactionCreator(sipProvider); sipSecurityManager.setSipManCallback(this); // Make sure prebuilt headers are nulled so that they get reinited // if this is a restart contactHeader = null; fromHeader = null; viaHeaders = null; maxForwardsHeader = null; isStarted = true; } finally { } } /** * Unregisters listening points, deletes sip providers, and generally * prepares the stack for a re-start(). This method is meant to be used when * properties are changed and should be reread by the stack. * * @throws CommunicationsException */ synchronized public void stop() throws CommunicationsException { try { if (registerProcessing != null) registerProcessing.cancelSchedules(); if (sipStack == null) return; sleep(500); sipProvider.removeSipListener(this); // Delete SipProvider int tries = 0; for (tries = 0; tries < RETRY_OBJECT_DELETES; tries++) { try { sipStack.deleteSipProvider(sipProvider); } catch (ObjectInUseException ex) { sleep(RETRY_OBJECT_DELETES_AFTER); continue; } break; } if (sipStack == null) return; if (tries >= RETRY_OBJECT_DELETES) throw new CommunicationsException( "Failed to delete the sipProvider!"); if (sipStack == null) return; // Delete RI ListeningPoint for (tries = 0; tries < RETRY_OBJECT_DELETES; tries++) { try { sipStack.deleteListeningPoint(listeningPoint); } catch (ObjectInUseException ex) { // Log.debug("Retrying delete of riListeningPoint!"); sleep(RETRY_OBJECT_DELETES_AFTER); continue; } break; } if (sipStack != null) { for (Iterator<?> it = sipStack.getSipProviders(); it.hasNext();) { SipProvider element = (SipProvider) it.next(); try { sipStack.deleteSipProvider(element); } catch (Exception e) { } } } if (tries >= RETRY_OBJECT_DELETES) throw new CommunicationsException( "Failed to delete a listeningPoint!"); listeningPoint = null; addressFactory = null; messageFactory = null; headerFactory = null; sipStack = null; registrarAddress = null; securityAuthority = null; viaHeaders = null; contactHeader = null; fromHeader = null; NetworkAddressManager.shutDown(); } finally { isStarted = false; } } /** * Waits during _no_less_ than sleepFor milliseconds. Had to implement it on * top of Thread.sleep() to guarantee minimum sleep time. * * @param sleepFor the number of miliseconds to wait */ protected static void sleep(long sleepFor) { long startTime = System.currentTimeMillis(); long haveBeenSleeping = 0; while (haveBeenSleeping < sleepFor) { try { Thread.sleep(sleepFor - haveBeenSleeping); } catch (InterruptedException ex) { // we-ll have to wait again! } haveBeenSleeping = (System.currentTimeMillis() - startTime); } } /** * @param uri the currentlyUsedURI to set. */ public void setCurrentlyUsedURI(String uri) { this.currentlyUsedURI = uri; } /** * Get the currently used SIP username */ public String getCurrentUsername() { return this.username; } /** * Causes the RegisterProcessing object to send a registration request to * the registrar defined in net.java.mais.sip.REGISTRAR_ADDRESS and to * register with the address defined in the net.java.mais.sip.PUBLIC_ADDRESS * property * * @throws CommunicationsException if an exception is thrown by the underlying stack. The * exception that caused this CommunicationsException may be * extracted with CommunicationsException.getCause() */ public void register() throws CommunicationsException { register(currentlyUsedURI); } /** * Registers using the specified public address. * * @param sipServerAddress the address of the server. * @throw CommunicationsException */ public void register(String sipServerAddress) throws CommunicationsException { try { if (sipServerAddress == null || sipServerAddress.trim().length() == 0) { Log.debug("PUBLIC NOT FOUND!"); throw new CommunicationsException("Public address NOT FOUND"); } // Handle default domain name (i.e. transform 1234 -> 1234@sip.com String defaultDomainName = SIPConfig.getDefaultDomain(); if (sipServerAddress.toLowerCase().indexOf("sipphone.com") != -1 || (defaultDomainName != null && defaultDomainName .indexOf("sipphone.com") != -1)) { StringBuffer buff = new StringBuffer(sipServerAddress); int nameEnd = sipServerAddress.indexOf('@'); nameEnd = nameEnd == -1 ? Integer.MAX_VALUE : nameEnd; nameEnd = Math.min(nameEnd, buff.length()) - 1; int nameStart = sipServerAddress.indexOf("sip:"); nameStart = nameStart == -1 ? 0 : nameStart + "sip:".length(); for (int i = nameEnd; i >= nameStart; i--) if (!Character.isLetter(buff.charAt(i)) && !Character.isDigit(buff.charAt(i))) buff.deleteCharAt(i); sipServerAddress = buff.toString(); } // if user didn't provide a domain name in the URL and someone // has defined the DEFAULT_DOMAIN_NAME property - let's fill in the // blank. if (defaultDomainName != null && sipServerAddress.indexOf('@') == -1) { sipServerAddress = sipServerAddress + "@" + defaultDomainName; } if (!sipServerAddress.trim().toLowerCase().startsWith("sip:")) { sipServerAddress = "sip:" + sipServerAddress; } this.currentlyUsedURI = sipServerAddress; registerProcessing.register(registrarAddress, registrarPort, registrarTransport, registrationsExpiration); } catch (Exception e) { fireRegistrationFailed(registrarAddress, RegistrationEvent.Type.TimeOut); Log.error("register", e); } } public void startRegisterProcess(String userName, String authUserName, String password) throws CommunicationsException { try { // TODO : Anpassen //SIPTransaction.setRegisterTimeout(10); //registerRetries = 0; checkIfStarted(); // Obtain initial credentials Credentials defaultCredentials = new Credentials(); this.username = userName; defaultCredentials.setUserName(userName); defaultCredentials.setPassword(password.toCharArray()); defaultCredentials .setAuthUserName(authUserName != null ? authUserName : userName); String realm = SIPConfig.getAuthenticationRealm(); realm = realm == null ? "" : realm; Credentials initialCredentials = securityAuthority .obtainCredentials(realm, defaultCredentials); SIPConfig.setUserName(initialCredentials.getUserName()); SIPConfig.setAuthUserName(initialCredentials.getAuthUserName()); register(initialCredentials.getUserName()); // at this point a simple register request has been sent and the // global // from header in SipManager has been set to a valid value by the // RegisterProcesing // class. Use it to extract the valid user name that needs to be // cached by // the security manager together with the user provided password. //initialCredentials.setUserName(((SipURI) getFromHeader().getAddress().getURI()).getUser()); cacheCredentials(realm, initialCredentials); } catch (Exception ee) { Log.error("startRegisterProcess", ee); } } /** * Causes the PresenceAgent object to notify all subscribers of our brand * new offline status and the RegisterProcessing object to send a * registration request with a 0 "expires" interval to the registrar defined * in net.java.mais.sip.REGISTRAR_ADDRESS. * * @throws CommunicationsException if an exception is thrown by the underlying stack. The * exception that caused this CommunicationsException may be * extracted with CommunicationsException.getCause() */ public void unregister() throws CommunicationsException { try { checkIfStarted(); if (!isRegistered()) { return; } registerProcessing.unregister(); fireUnregistered(registrarAddress == null ? "" : registrarAddress); } catch (Exception e) { Log.error("unregister", e); } } private boolean registrationFailed(RegistrationEvent.Type type) { try { //if (RegistrationEvent.Type.TimeOut.equals(type)) if (registerRetries++ < 2) { stop(); while (isStarted()) ; start(); while (!isStarted()) ; register(); return false; } else { if (NetworkAddressManager.nextIndex()) { stop(); start(); registerRetries = 0; register(); return false; } } } catch (Exception e) { Log.error("unregister", e); } return true; } /** * Queries the RegisterProcessing object whether the application is * registered with a registrar. * * @return true if the application is registered with a registrar. */ public boolean isRegistered() { return (registerProcessing != null && registerProcessing.isRegistered()); } /** * Determines whether the SipManager was started. * * @return true if the SipManager was started. */ public boolean isStarted() { return isStarted; } // ============================ COMMUNICATION FUNCTIONALITIES ========================= /** * Causes the CallProcessing object to send an INVITE request to the URI * specified by <code>callee</code> setting sdpContent as message body. * The method generates a Call object that will represent the resulting call * and will be used for later references to the same call. * * @param callee the URI to send the INVITE to. * @param sdpContent the sdp session offer. * @return the Call object that will represent the call resulting from * invoking this method. * @throws CommunicationsException if an exception occurs while sending and parsing. */ public Call establishCall(String callee, String sdpContent) throws CommunicationsException { checkIfStarted(); if (isBusy()) return null; return callProcessing.invite(callee, sdpContent); } // CALL // ------------------ hang up on /** * Causes the CallProcessing object to send a terminating request (CANCEL, * BUSY_HERE or BYE) and thus terminate that call with id * <code>callID</code>. * * @param callID the id of the call to terminate. * @throws CommunicationsException if an exception occurs while invoking this method. */ public void endCall(int callID) throws CommunicationsException { fireCallEnded(callProcessing.callDispatcher.getCall(callID)); checkIfStarted(); callProcessing.endCall(callID); } public void sendDTMF(int callID, String digit) throws CommunicationsException { callProcessing.sendDTMF(callID, digit); } public void hold(int callID, SessionDescription sdp, boolean mic, boolean cam) throws CommunicationsException { callProcessing.hold(callID, sdp.toString()); callProcessing.callDispatcher.getCall(callID).onHoldMic(mic); callProcessing.callDispatcher.getCall(callID).onHoldCam(cam); } public void transfer(int callID, String callee) { try { transferProcessing.transfer(callID, callee); } catch (CommunicationsException e) { e.printStackTrace(); } } /** * Calls endCall for all currently active calls. * * @throws CommunicationsException if an exception occurs while */ public void endAllCalls() throws CommunicationsException { try { checkIfStarted(); if (!isRegistered()) { return; } if (callProcessing == null) { return; } Object[] keys = callProcessing.getCallDispatcher().getAllCalls(); for (int i = 0; i < keys.length; i++) { endCall(((Integer) keys[i]).intValue()); } } finally { setBusy(false); } } /** * Causes CallProcessing to send a 200 OK response, with the specified sdp * description, to the specified call's remote party. * * @param callID the id of the call that is to be answered. * @param sdpContent this party's media description (as defined by SDP). * @throws CommunicationsException if an axeption occurs while invoking this method. */ public void answerCall(int callID, String sdpContent) throws CommunicationsException { checkIfStarted(); callProcessing.sayOK(callID, sdpContent); } // answer to /** * Sends a NOT_IMPLEMENTED response through the specified transaction. * * @param serverTransaction the transaction to send the response through. * @param request the request that is being answered. */ void sendNotImplemented(ServerTransaction serverTransaction, Request request) { Response notImplemented = null; try { notImplemented = messageFactory.createResponse( Response.NOT_IMPLEMENTED, request); attachToTag(notImplemented, serverTransaction.getDialog()); } catch (ParseException ex) { fireCommunicationsError(new CommunicationsException( "Failed to create a NOT_IMPLEMENTED response to a " + request.getMethod() + " request!", ex)); return; } try { serverTransaction.sendResponse(notImplemented); } catch (SipException ex) { fireCommunicationsError(new CommunicationsException( "Failed to create a NOT_IMPLEMENTED response to a " + request.getMethod() + " request!", ex)); } catch (InvalidArgumentException e) { fireCommunicationsError(new CommunicationsException( "Failed to create a NOT_IMPLEMENTED response to a " + request.getMethod() + " request!", e)); } } public void processIOException(IOExceptionEvent ioExceptionEvent) { } public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) { } public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { } // ============================= Utility Methods ================================== /** * Initialises SipManager's fromHeader field in accordance with * net.java.mais.sip.PUBLIC_ADDRESS net.java.mais.sip.DISPLAY_NAME * net.java.mais.sip.TRANSPORT net.java.mais.sip.PREFERRED_LOCAL_PORT and * returns a reference to it. * * @return a reference to SipManager's fromHeader field. * @throws CommunicationsException if a ParseException occurs while initially composing the * FromHeader. */ public FromHeader getFromHeader() throws CommunicationsException { return this.getFromHeader(false); } public FromHeader getFromHeader(boolean isNew) throws CommunicationsException { if (fromHeader != null && !isNew) { return fromHeader; } try { SipURI fromURI = (SipURI) addressFactory .createURI(currentlyUsedURI); fromURI.setTransportParam(listeningPoint.getTransport()); fromURI.setPort(listeningPoint.getPort()); Address fromAddress = addressFactory.createAddress(fromURI); if (displayName != null && displayName.trim().length() > 0) { fromAddress.setDisplayName(displayName); } else { fromAddress .setDisplayName(Credentials.getUserDisplay()); } fromHeader = headerFactory.createFromHeader(fromAddress, Integer.toString(hashCode())); } catch (ParseException ex) { throw new CommunicationsException( "A ParseException occurred while creating From Header!", ex); } return fromHeader; } /** * Same as calling getContactHeader(true) * * @return the result of getContactHeader(true) * @throws CommunicationsException if an exception is thrown while calling * getContactHeader(false) * @uml.property name="contactHeader" */ public ContactHeader getContactHeader() throws CommunicationsException { return getContactHeader(true); } /** * Same as calling getContactHeader(true). * * @return the result of calling getContactHeader(true). * @throws CommunicationsException if an exception occurs while executing * getContactHeader(true). */ ContactHeader getRegistrationContactHeader() throws CommunicationsException { return getContactHeader(true); } /** * Initialises SipManager's contactHeader field in accordance with * javax.sip.IP_ADDRESS net.java.mais.sip.DISPLAY_NAME * net.java.mais.sip.TRANSPORT net.java.mais.sip.PREFERRED_LOCAL_PORT and * returns a reference to it. * * @param useLocalHostAddress specifies whether the SipURI in the contact header should * contain the value of javax.sip.IP_ADDRESS (true) or that of * net.java.mais.sip.PUBLIC_ADDRESS (false). * @return a reference to SipManager's contactHeader field. * @throws CommunicationsException if a ParseException occurs while initially composing the * FromHeader. */ public ContactHeader getContactHeader(boolean useLocalHostAddress) throws CommunicationsException { if (contactHeader != null) { return contactHeader; } try { SipURI contactURI; if (useLocalHostAddress) { contactURI = (SipURI) addressFactory.createSipURI(null, Credentials.getUserDisplay() + "@" + publicIpAddress.getAddress() .getHostAddress()); } else { contactURI = (SipURI) addressFactory .createURI(currentlyUsedURI); } // Forces NAT Traversal // contactURI = (SipURI) // addressFactory.createSipURI(null,Credentials.getUser()+ // "@" + publicIpAddress.getAddress().getHostAddress()); // contactURI.setTransportParam(listeningPoint.getTransport()); contactURI.setPort(publicIpAddress.getPort()); Address contactAddress = addressFactory .createAddress(contactURI); if (displayName != null && displayName.trim().length() > 0) { contactAddress.setDisplayName(displayName); } contactHeader = headerFactory .createContactHeader(contactAddress); } catch (ParseException ex) { throw new CommunicationsException( "A ParseException occurred while creating From Header!", ex); } return contactHeader; } /** * Initializes (if null) and returns an ArrayList with a single ViaHeader * containing localhost's address. This ArrayList may be used when sending * requests. * * @return ViaHeader-s list to be used when sending requests. * @throws CommunicationsException if a ParseException is to occur while initializing the array * list. */ public ArrayList<ViaHeader> getLocalViaHeaders() throws CommunicationsException { ListeningPoint lp = sipProvider.getListeningPoints()[0]; viaHeaders = new ArrayList<ViaHeader>(); try { ViaHeader viaHeader = headerFactory.createViaHeader(SIPConfig .getIPAddress(), lp.getPort(), lp.getTransport(), null); viaHeader.setParameter("rport", null); viaHeaders.add(viaHeader); return viaHeaders; } catch (ParseException ex) { throw new CommunicationsException( "A ParseException occurred while creating Via Headers!"); } catch (InvalidArgumentException ex) { throw new CommunicationsException( "Unable to create a via header for port " + lp.getPort(), ex); } } /** * Initializes and returns SipManager's maxForwardsHeader field using the * value specified by MAX_FORWARDS. * * @return an instance of a MaxForwardsHeader that can be used when sending * requests * @throws CommunicationsException if MAX_FORWARDS has an invalid value. */ public MaxForwardsHeader getMaxForwardsHeader() throws CommunicationsException { if (maxForwardsHeader != null) { return maxForwardsHeader; } try { maxForwardsHeader = headerFactory .createMaxForwardsHeader(MAX_FORWARDS); return maxForwardsHeader; } catch (InvalidArgumentException ex) { throw new CommunicationsException( "A problem occurred while creating MaxForwardsHeader", ex); } } /** * Returns the user used to create the From Header URI. * * @return the user used to create the From Header URI. */ public String getLocalUser() { try { return ((SipURI) getFromHeader().getAddress().getURI()).getUser(); } catch (CommunicationsException ex) { return ""; } } /** * Generates a ToTag (the containingDialog's hashCode())and attaches it to * response's ToHeader. * * @param response the response that is to get the ToTag. * @param containingDialog the Dialog instance that is to extract a unique Tag value * (containingDialog.hashCode()) */ public void attachToTag(Response response, Dialog containingDialog) { ToHeader to = (ToHeader) response.getHeader(ToHeader.NAME); if (to == null) { fireCommunicationsError(new CommunicationsException( "No TO header found in, attaching a to tag is therefore impossible")); } try { if (to.getTag() == null || to.getTag().trim().length() == 0) { // the containing dialog may be null (e.g. when called by // sendNotImplemented). Attach sth else in that case. // Bug Report - Joe Provino - SUN Microsystems int toTag = containingDialog != null ? containingDialog .hashCode() : (int) System.currentTimeMillis(); to.setTag(Integer.toString(toTag)); } } catch (ParseException ex) { fireCommunicationsError(new CommunicationsException( "Failed to attach a TO tag to an outgoing response")); } } // ================================ PROPERTIES ================================ protected void initProperties() { try { // ------------------ stack properties -------------- // network address management is handled by // common.NetworkAddressManager // stackAddress = Utils.getProperty("javax.sip.IP_ADDRESS"); // if (stackAddress == null) { stackAddress = getLocalHostAddress(); // Add the host address to the properties that will pass the stack SIPConfig.setIPAddress(stackAddress); SIPConfig.setSystemProperties(); // ensure IPv6 address compliance if (stackAddress.indexOf(':') != stackAddress.lastIndexOf(':') && stackAddress.charAt(0) != '[') { stackAddress = '[' + stackAddress.trim() + ']'; } stackName = SIPConfig.getStackName(); if (stackName == null) { stackName = "SIPark@" + Integer.toString(hashCode()); // Add the stack name to the properties that will pass the stack } // String retransmissionFilter = SIPConfig.getRetransmissionFilter(); // ------------ application properties -------------- //currentlyUsedURI = SIPConfig.getPublicAddress(); if (currentlyUsedURI == null) { currentlyUsedURI = SIPConfig.getUserName() + "@" + stackAddress; } if (!currentlyUsedURI.trim().toLowerCase().startsWith("sip:")) { currentlyUsedURI = "sip:" + currentlyUsedURI.trim(); } // at this point we are sure we have a sip: prefix in the uri // we construct our pres: uri by replacing that prefix. // String presenceUri = "pres" // + currentlyUsedURI.substring(currentlyUsedURI.indexOf(':')); registrarAddress = SIPConfig.getRegistrarAddress(); try { registrarPort = SIPConfig.getRegistrarPort(); } catch (NumberFormatException ex) { registrarPort = 5060; } registrarTransport = SIPConfig.getRegistrarTransport(); if (registrarTransport == null) { registrarTransport = DEFAULT_TRANSPORT; } try { registrationsExpiration = SIPConfig.getRegistrationExpiration(); } catch (NumberFormatException ex) { registrationsExpiration = 3600; } sipStackPath = SIPConfig.getStackPath(); if (sipStackPath == null) { sipStackPath = "gov.nist"; } // String routerPath = SIPConfig.getRouterPath(); transport = SIPConfig.getTransport(); if (transport.equals("")) { transport = DEFAULT_TRANSPORT; } try { localPort = SIPConfig.getLocalPort(); } catch (NumberFormatException exc) { localPort = 5060; } displayName = SIPConfig.getDisplayName(); } catch (Exception e) { Log.error("initProperties", e); } } // ============================ SECURITY ================================ /** * Sets the SecurityAuthority instance that should be consulted later on for * user credentials. * * @param authority the SecurityAuthority instance that should be consulted later * on for user credentials. */ public void setSecurityAuthority(SecurityAuthority authority) { // keep a copy this.securityAuthority = authority; sipSecurityManager.setSecurityAuthority(authority); } /** * Adds the specified credentials to the security manager's credentials * cache so that they get tried next time they're needed. * * @param realm the realm these credentials should apply for. * @param credentials a set of credentials (username and pass) */ public void cacheCredentials(String realm, Credentials credentials) { sipSecurityManager.cacheCredentials(realm, credentials); } // ============================ EVENT DISPATHING ============================ /** * Adds a CommunicationsListener to SipManager. * * @param listener The CommunicationsListener to be added. */ public void addCommunicationsListener(CommunicationsListener listener) { try { listeners.add(listener); } catch (Exception e) { Log.error("addCommunicationsListener", e); } } // ------------ call received dispatch void fireCallReceived(Call call) { try { CallEvent evt = new CallEvent(call); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)).callReceived(evt); } } catch (Exception e) { Log.error("fireCallReceived", e); } } // call received void fireCallEnded(Call call) { try { CallEvent evt = new CallEvent(call); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)).callEnded(evt); } } catch (Exception e) { Log.error("fireCallEnded", e); } } // call received // ------------ call received dispatch void fireMessageReceived(Request message) { MessageEvent evt = new MessageEvent(message); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)) .messageReceived(evt); } } // call received // ------------ registerred void fireRegistered(String address) { // TODO : Anpassen //SIPTransaction.setRegisterTimeout(64); try { registerProcessing.subscribe(registrarAddress, registrarPort, registrarTransport); } catch (CommunicationsException e) { Log.error(e); } RegistrationEvent evt = new RegistrationEvent(address); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)).registered(evt); } } // call received // ------------ registering void fireRegistering(String address) { RegistrationEvent evt = new RegistrationEvent(address); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)).registering(evt); } } // call received // ------------ unregistered public void fireUnregistered(String address) { RegistrationEvent evt = new RegistrationEvent(address); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)).unregistered(evt); } } void fireRegistrationFailed(String address, RegistrationEvent.Type type) { if (registrationFailed(type)) { RegistrationEvent evt = new RegistrationEvent(address, type); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)).registrationFailed(evt); } } } void fireUnregistering(String address) { RegistrationEvent evt = new RegistrationEvent(address); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)).unregistering(evt); } } // ---------------- received unknown message void fireUnknownMessageReceived(Message message) { UnknownMessageEvent evt = new UnknownMessageEvent(message); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)) .receivedUnknownMessage(evt); } } // ---------------- rejected a call public void fireCallRejectedLocally(String reason, Message invite, Call call) { CallRejectedEvent evt = new CallRejectedEvent(reason, invite, call); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)) .callRejectedLocally(evt); } } void fireCallRejectedRemotely(String reason, Message invite, Call call) { CallRejectedEvent evt = new CallRejectedEvent(reason, invite, call); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)) .callRejectedRemotely(evt); } } // call rejected // ---------------- error occurred public void fireCommunicationsError(Throwable throwable) { try { CommunicationsErrorEvent evt = new CommunicationsErrorEvent( throwable); for (int i = listeners.size() - 1; i >= 0; i--) { ((CommunicationsListener) listeners.get(i)).communicationsErrorOccurred(evt); } } catch (Throwable e) { Log.error("fireCommunicationsError", e); } } // error occurred // ============================= SIP LISTENER METHODS ============================== public void processRequest(RequestEvent requestReceivedEvent) { Log.debug(requestReceivedEvent.getRequest().toString()); ServerTransaction serverTransaction = requestReceivedEvent .getServerTransaction(); Request request = requestReceivedEvent.getRequest(); if (serverTransaction == null) { try { serverTransaction = sipProvider .getNewServerTransaction(request); } catch (TransactionAlreadyExistsException ex) { return; } catch (TransactionUnavailableException ex) { return; } } Dialog dialog = serverTransaction.getDialog(); if (request.getMethod().equals(Request.NOTIFY)) { Response ok = null; try { ok = messageFactory.createResponse(Response.OK, request); } catch (ParseException ex) { ex.printStackTrace(); } ContactHeader contactHeader = null; try { contactHeader = getContactHeader(); ok.addHeader(contactHeader); attachToTag(ok, dialog); System.err.println(ok.toString()); serverTransaction.sendResponse(ok); } catch (CommunicationsException e) { e.printStackTrace(); } catch (InvalidArgumentException e) { e.printStackTrace(); } catch (SipException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } fireMessageReceived(request); return; } //TODO REMOVE @SuppressWarnings("unused") Request requestClone = (Request) request.clone(); // INVITE if (request.getMethod().equals(Request.INVITE)) { if (serverTransaction.getDialog().getState() == null) { callProcessing.processInvite(serverTransaction, request); } else { callProcessing.processReInvite(serverTransaction, request); } } // ACK else if (request.getMethod().equals(Request.ACK)) { if (serverTransaction != null && serverTransaction.getDialog().getFirstTransaction() .getRequest().getMethod() .equals(Request.INVITE)) { callProcessing.processAck(serverTransaction, request); } else { // just ignore } } // BYE else if (request.getMethod().equals(Request.BYE)) { if (dialog.getFirstTransaction().getRequest().getMethod() .equals(Request.INVITE) || dialog.getFirstTransaction().getRequest().getMethod() .equals(Request.REFER)) { callProcessing.processBye(serverTransaction, request); } } // CANCEL else if (request.getMethod().equals(Request.CANCEL)) { if (dialog.getFirstTransaction().getRequest().getMethod() .equals(Request.INVITE)) { callProcessing.processCancel(serverTransaction, request); } else { sendNotImplemented(serverTransaction, request); fireUnknownMessageReceived(requestReceivedEvent .getRequest()); } } // REFER else if (request.getMethod().equals(Request.REFER)) { transferProcessing.processRefer(serverTransaction, request); } else if (request.getMethod().equals(Request.INFO)) { /** @todo add proper request handling */ fireMessageReceived(request); } else if (request.getMethod().equals(Request.MESSAGE)) { messageProcessing.processMessageRequest(serverTransaction, request); fireMessageReceived(request); } else if (request.getMethod().equals(Request.OPTIONS)) { /** @todo add proper request handling */ sendNotImplemented(serverTransaction, request); fireUnknownMessageReceived(requestReceivedEvent.getRequest()); } else if (request.getMethod().equals(Request.PRACK)) { /** @todo add proper request handling */ sendNotImplemented(serverTransaction, request); fireUnknownMessageReceived(requestReceivedEvent.getRequest()); } else if (request.getMethod().equals(Request.REGISTER)) { /** @todo add proper request handling */ sendNotImplemented(serverTransaction, request); fireUnknownMessageReceived(requestReceivedEvent.getRequest()); } else if (request.getMethod().equals(Request.SUBSCRIBE)) { } else if (request.getMethod().equals(Request.UPDATE)) { /** @todo add proper request handling */ sendNotImplemented(serverTransaction, request); fireUnknownMessageReceived(requestReceivedEvent.getRequest()); } else if (request.getMethod().equals(Request.INFO)) { sendNotImplemented(serverTransaction, request); fireUnknownMessageReceived(requestReceivedEvent.getRequest()); } else { // We couldn't recognise the message sendNotImplemented(serverTransaction, request); fireUnknownMessageReceived(requestReceivedEvent.getRequest()); } } public void processTimeout(TimeoutEvent transactionTimeOutEvent) { Transaction transaction; if (transactionTimeOutEvent.isServerTransaction()) { transaction = transactionTimeOutEvent.getServerTransaction(); } else { transaction = transactionTimeOutEvent.getClientTransaction(); } Request request = transaction.getRequest(); if (request.getMethod().equals(Request.REGISTER)) { registerProcessing.processTimeout(transaction, request); } else if (request.getMethod().equals(Request.INVITE)) { callProcessing.processTimeout(transaction, request); } else { // Just show an error for now } } // -------------------- PROCESS RESPONSE public void processResponse(ResponseEvent responseReceivedEvent) { Log.debug("<RESPONSE>"); Log.debug("[" + responseReceivedEvent.getResponse().getStatusCode() + "]"); Log.debug("</RESPONSE>"); ClientTransaction clientTransaction = responseReceivedEvent .getClientTransaction(); if (clientTransaction == null) { return; } Response response = responseReceivedEvent.getResponse(); //TODO REMOVE @SuppressWarnings("unused") Dialog dialog = clientTransaction.getDialog(); String method = ((CSeqHeader) response.getHeader(CSeqHeader.NAME)) .getMethod(); //TODO REMOVE @SuppressWarnings("unused") Response responseClone = (Response) response.clone(); // OK if (response.getStatusCode() == Response.OK) { // REGISTER if (method.equals(Request.REGISTER)) { registerProcessing.processOK(clientTransaction, response); }// INVITE else if (method.equals(Request.INVITE)) { callProcessing.processInviteOK(clientTransaction, response); }// BYE else if (method.equals(Request.BYE)) { callProcessing.processByeOK(clientTransaction, response); }// CANCEL else if (method.equals(Request.CANCEL)) { callProcessing.processCancelOK(clientTransaction, response); } else if (method.equals(Request.SUBSCRIBE)) { } } // SESSION PROGRESS else if (response.getStatusCode() == Response.SESSION_PROGRESS) { callProcessing.processRingingBack(clientTransaction, response); } // ACCEPTED else if (response.getStatusCode() == Response.ACCEPTED) { // SUBSCRIBE if (method.equals(Request.SUBSCRIBE)) { } else if (method.equals(Request.REFER)) { Call call = callProcessing.getCallDispatcher().findCall(clientTransaction.getDialog()); try { callProcessing.endCall(call); } catch (CommunicationsException e) { Log.error("Trasnfer", e); } } } // RINGING - process RINGING before treating the general case of a // provisional response (see next "else if") // report and fix - Asa Karlsson else if (response.getStatusCode() == Response.RINGING) { if (method.equals(Request.INVITE)) { callProcessing.processRinging(clientTransaction, response); } else { fireUnknownMessageReceived(response); } } // TRYING else if (response.getStatusCode() == Response.TRYING // process all provisional responses here // reported by Les Roger Davis || response.getStatusCode() / 100 == 1) { if (method.equals(Request.INVITE)) { callProcessing.processTrying(clientTransaction, response); } // We could also receive a TRYING response to a REGISTER req // bug reports by // Steven Lass <sltemp at comcast.net> // Luis Vazquez <luis at teledata.com.uy> else if (method.equals(Request.REGISTER)) { // do nothing } else { // TODO TRYING // fireUnknownMessageReceived(response); } } // NOT_FOUND else if (response.getStatusCode() == Response.NOT_FOUND) { if (method.equals(Request.INVITE)) { callProcessing.processNotFound(clientTransaction, response); } if (method.equals(Request.SUBSCRIBE)) { } else if (method.equals(Request.REGISTER)) { fireRegistrationFailed("Invalid username.", RegistrationEvent.Type.NotFound); Log.debug("REGISTER NOT FOUND"); } } // NOT_IMPLEMENTED else if (response.getStatusCode() == Response.NOT_IMPLEMENTED) { if (method.equals(Request.REGISTER)) { // Fixed typo issues - Reported by pizarro registerProcessing.processNotImplemented(clientTransaction, response); } else if (method.equals(Request.INVITE)) { callProcessing.processNotImplemented(clientTransaction, response); } else { fireUnknownMessageReceived(response); } } // REQUEST_TERMINATED else if (response.getStatusCode() == Response.REQUEST_TERMINATED) { callProcessing.processRequestTerminated(clientTransaction, response); } // BUSY_HERE else if (response.getStatusCode() == Response.BUSY_HERE) { if (method.equals(Request.INVITE)) { callProcessing.processBusyHere(clientTransaction, response); } else { fireUnknownMessageReceived(response); } } // 401 UNAUTHORIZED else if (response.getStatusCode() == Response.UNAUTHORIZED || response.getStatusCode() == Response.PROXY_AUTHENTICATION_REQUIRED) { if (method.equals(Request.INVITE)) { callProcessing.processAuthenticationChallenge( clientTransaction, response); } else if (method.equals(Request.REGISTER)) { //TODO REMOVE @SuppressWarnings("unused") CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); registerProcessing.processAuthenticationChallenge( clientTransaction, response); loginfailed++ ; if (loginfailed < 3) Log.debug("Removed"); else fireRegistrationFailed("Invalid password.", RegistrationEvent.Type.WrongPass); } else if (method.equals(Request.SUBSCRIBE)) { } else fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.FORBIDDEN) { } else if (response.getStatusCode() == 403) { // Wrong Authorization User fireRegistrationFailed("Invalid auth user.", RegistrationEvent.Type.WrongAuthUser); } // Other Errors else if ( // We'll handle all errors the same way so no individual // handling // is needed // response.getStatusCode() == Response.NOT_ACCEPTABLE // || response.getStatusCode() == Response.SESSION_NOT_ACCEPTABLE response.getStatusCode() / 100 == 4) { if (method.equals(Request.INVITE)) { callProcessing .processCallError(clientTransaction, response); } else { fireUnknownMessageReceived(response); } } else if (response.getStatusCode() == Response.ACCEPTED) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.ADDRESS_INCOMPLETE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.ALTERNATIVE_SERVICE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.AMBIGUOUS) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.BAD_EVENT) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.BAD_EXTENSION) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.BAD_GATEWAY) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.BAD_REQUEST) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.BUSY_EVERYWHERE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.CALL_IS_BEING_FORWARDED) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST) { /** @todo add proper request handling */ // fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.DECLINE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.DOES_NOT_EXIST_ANYWHERE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.EXTENSION_REQUIRED) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.GONE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.INTERVAL_TOO_BRIEF) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.LOOP_DETECTED) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.MESSAGE_TOO_LARGE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.METHOD_NOT_ALLOWED) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.MOVED_PERMANENTLY) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.MOVED_TEMPORARILY) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.MULTIPLE_CHOICES) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.NOT_ACCEPTABLE_HERE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.PAYMENT_REQUIRED) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.QUEUED) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.REQUEST_ENTITY_TOO_LARGE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.REQUEST_PENDING) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.REQUEST_TIMEOUT) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.REQUEST_URI_TOO_LONG) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } // Thiago Rocha Camargo // Contorno para falha na plataforma else if (response.getStatusCode() == Response.SERVER_INTERNAL_ERROR) { // } else if (response.getStatusCode() == Response.SERVER_TIMEOUT) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.SERVICE_UNAVAILABLE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.SESSION_NOT_ACCEPTABLE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.SESSION_PROGRESS) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.TEMPORARILY_UNAVAILABLE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.TOO_MANY_HOPS) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.UNDECIPHERABLE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.UNSUPPORTED_MEDIA_TYPE) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.UNSUPPORTED_URI_SCHEME) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.USE_PROXY) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else if (response.getStatusCode() == Response.VERSION_NOT_SUPPORTED) { /** @todo add proper request handling */ fireUnknownMessageReceived(response); } else { // We couldn't recognise the message fireUnknownMessageReceived(response); } } // process response // -------- String getLocalHostAddress() { // network address management is handled by // common.NetworkAddressManager // String hostAddress = Utils.getProperty("javax.sip.IP_ADDRESS"); // if (hostAddress == null) { InetAddress localhost = NetworkAddressManager.getLocalHost(); String hostAddress = localhost.getHostAddress(); return hostAddress; } protected void checkIfStarted() throws CommunicationsException { if (!isStarted) { throw new CommunicationsException( "The underlying SIP Stack had not been" + "properly initialised! Impossible to continue"); } } public void sendServerInternalError(int callID) throws CommunicationsException { checkIfStarted(); callProcessing.sayInternalError(callID); } public boolean isBusy() { return isBusy; } public void setBusy(boolean busy) { isBusy = busy; } }