content: /*
   * Conditions Of Use
   *
   * This software was developed by employees of the National Institute of
   * Standards and Technology (NIST), an agency of the Federal Government.
   * Pursuant to title 15 Untied States Code Section 105, works of NIST
   * employees are not subject to copyright protection in the United States
   * and are considered to be in the public domain.  As a result, a formal
   * license is not needed to use the software.
   *
   * This software is provided by NIST as a service and is expressly
   * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
   * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
   * AND DATA ACCURACY.  NIST does not warrant or make any representations
   * regarding the use of the software or the results thereof, including but
   * not limited to the correctness, accuracy, reliability or usefulness of
   * the software.
   *
   * Permission to use this software is contingent upon your acceptance
   * of the terms of this agreement
   *
   * .
   *
   */
  /******************************************************************************
   * Product of NIST/ITL Advanced Networking Technologies Division (ANTD).      *
   ******************************************************************************/
  package gov2.nist.javax2.sip;
  
  import gov2.nist.core.InternalErrorHandler;
  import gov2.nist.javax2.sip.DialogTimeoutEvent.Reason;
  import gov2.nist.javax2.sip.address.RouterExt;
  import gov2.nist.javax2.sip.header.CallID;
  import gov2.nist.javax2.sip.header.Via;
  import gov2.nist.javax2.sip.message.SIPMessage;
  import gov2.nist.javax2.sip.message.SIPRequest;
  import gov2.nist.javax2.sip.message.SIPResponse;
  import gov2.nist.javax2.sip.stack.HopImpl;
  import gov2.nist.javax2.sip.stack.MessageChannel;
  import gov2.nist.javax2.sip.stack.SIPClientTransaction;
  import gov2.nist.javax2.sip.stack.SIPDialog;
  import gov2.nist.javax2.sip.stack.SIPDialogErrorEvent;
  import gov2.nist.javax2.sip.stack.SIPDialogEventListener;
  import gov2.nist.javax2.sip.stack.SIPServerTransaction;
  import gov2.nist.javax2.sip.stack.SIPTransaction;
  import gov2.nist.javax2.sip.stack.SIPTransactionErrorEvent;
  import gov2.nist.javax2.sip.stack.SIPTransactionEventListener;
  
  import java.io.IOException;
  import java.text.ParseException;
  import java.util.EventObject;
  import java.util.Iterator;
  import java.util.TooManyListenersException;
  import java.util.concurrent.ConcurrentHashMap;
  
  import javax2.sip.ClientTransaction;
  import javax2.sip.Dialog;
  import javax2.sip.DialogState;
  import javax2.sip.InvalidArgumentException;
  import javax2.sip.ListeningPoint;
  import javax2.sip.ObjectInUseException;
  import javax2.sip.RequestEvent;
  import javax2.sip.ResponseEvent;
  import javax2.sip.ServerTransaction;
  import javax2.sip.SipException;
  import javax2.sip.SipListener;
  import javax2.sip.SipStack;
  import javax2.sip.Timeout;
  import javax2.sip.TimeoutEvent;
  import javax2.sip.Transaction;
  import javax2.sip.TransactionAlreadyExistsException;
  import javax2.sip.TransactionState;
  import javax2.sip.TransactionUnavailableException;
  import javax2.sip.address.Hop;
  import javax2.sip.header.CallIdHeader;
  import javax2.sip.message.Request;
  import javax2.sip.message.Response;
  
  /*
   * Contributions (bug fixes) made by: Daniel J. Martinez Manzano, Hagai Sela.
   * Bug reports by Shanti Kadiyala, Rhys Ulerich,Victor Hugo
   */
  /**
   * Implementation of the JAIN-SIP provider interface.
   *
   * @version 1.2 $Revision: 1.82 $ $Date: 2009/11/24 17:16:59 $
   *
   * @author M. Ranganathan <br/>
   *
   *
   */
  
  public class SipProviderImpl implements javax2.sip.SipProvider, gov2.nist.javax2.sip.SipProviderExt,
          SIPTransactionEventListener, SIPDialogEventListener {
  
      private SipListener sipListener;
  
      protected SipStackImpl sipStack;
  
      /*
       * A set of listening points associated with the provider At most one LP per
       * transport
       */
      private ConcurrentHashMap listeningPoints;
  
      private EventScanner eventScanner;
  
      private String address;
  
      private int port;
  
      private boolean automaticDialogSupportEnabled ; 
      /**
       * A string containing the 0.0.0.0 IPv4 ANY address.
       */
      private String IN_ADDR_ANY = "0.0.0.0";
  
      /**
       * A string containing the ::0 IPv6 ANY address.
       */
      private String IN6_ADDR_ANY = "::0";
  
      private boolean dialogErrorsAutomaticallyHandled = true;
      
      private SipProviderImpl() {
      	
      }
  
      /**
       * Stop processing messages for this provider. Post an empty message to our
       * message processing queue that signals us to quit.
       */
      protected void stop() {
          // Put an empty event in the queue and post ourselves a message.
          if (sipStack.isLoggingEnabled())
              sipStack.getStackLogger().logDebug("Exiting provider");
          for (Iterator it = listeningPoints.values().iterator(); it.hasNext();) {
              ListeningPointImpl listeningPoint = (ListeningPointImpl) it.next();
              listeningPoint.removeSipProvider();
          }
          this.eventScanner.stop();
  
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#getListeningPoint(java.lang.String)
       */
      public ListeningPoint getListeningPoint(String transport) {
          if (transport == null)
              throw new NullPointerException("Null transport param");
          return (ListeningPoint) this.listeningPoints.get(transport
                  .toUpperCase());
      }
  
      /**
       * Handle the SIP event - because we have only one listener and we are
       * already in the context of a separate thread, we dont need to enque the
       * event and signal another thread.
       *
       * @param sipEvent
       *            is the event to process.
       *
       */
  
      public void handleEvent(EventObject sipEvent, SIPTransaction transaction) {
          if (sipStack.isLoggingEnabled()) {
              sipStack.getStackLogger().logDebug(
                      "handleEvent " + sipEvent + "currentTransaction = "
                              + transaction + "this.sipListener = "
                              + this.getSipListener() + "sipEvent.source = "
                              + sipEvent.getSource());
              if (sipEvent instanceof RequestEvent) {
                  Dialog dialog = ((RequestEvent) sipEvent).getDialog();
                  if ( sipStack.isLoggingEnabled())  sipStack.getStackLogger().logDebug("Dialog = " + dialog);
              } else if (sipEvent instanceof ResponseEvent) {
                  Dialog dialog = ((ResponseEvent) sipEvent).getDialog();
                  if (sipStack.isLoggingEnabled() ) sipStack.getStackLogger().logDebug("Dialog = " + dialog);
              }
              sipStack.getStackLogger().logStackTrace();
          }
  
          EventWrapper eventWrapper = new EventWrapper(sipEvent, transaction);
  
          if (!sipStack.reEntrantListener) {
              // Run the event in the context of a single thread.
              this.eventScanner.addEvent(eventWrapper);
          } else {
              // just call the delivery method
              this.eventScanner.deliverEvent(eventWrapper);
          }
      }
  
      /** Creates a new instance of SipProviderImpl */
      protected SipProviderImpl(SipStackImpl sipStack) {
          this.eventScanner = sipStack.getEventScanner(); // for quick access.
          this.sipStack = sipStack;
          this.eventScanner.incrementRefcount();
          this.listeningPoints = new ConcurrentHashMap<String,ListeningPointImpl>();
          this.automaticDialogSupportEnabled = this.sipStack
                  .isAutomaticDialogSupportEnabled();
          this.dialogErrorsAutomaticallyHandled = this.sipStack.isAutomaticDialogErrorHandlingEnabled();
      }
  
      /*
       * (non-Javadoc)
       *
       * @see java.lang.Object#clone()
       */
      protected Object clone() throws java.lang.CloneNotSupportedException {
          throw new java.lang.CloneNotSupportedException();
      }
  
      
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#addSipListener(javax2.sip.SipListener)
       */
      public void addSipListener(SipListener sipListener)
              throws TooManyListenersException {
  
          if (sipStack.sipListener == null) {
              sipStack.sipListener = sipListener;
          } else if (sipStack.sipListener != sipListener) {
              throw new TooManyListenersException(
                      "Stack already has a listener. Only one listener per stack allowed");
          }
  
          if (sipStack.isLoggingEnabled())
              sipStack.getStackLogger().logDebug("add SipListener " + sipListener);
          this.sipListener = sipListener;
  
      }
  
      /*
       * This method is deprecated (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#getListeningPoint()
       */
  
      public ListeningPoint getListeningPoint() {
          if (this.listeningPoints.size() > 0)
              return (ListeningPoint) this.listeningPoints.values().iterator()
                      .next();
          else
              return null;
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#getNewCallId()
       */
      public CallIdHeader getNewCallId() {
          String callId = Utils.getInstance().generateCallIdentifier(this.getListeningPoint()
                  .getIPAddress());
          CallID callid = new CallID();
          try {
              callid.setCallId(callId);
          } catch (java.text.ParseException ex) {
          }
          return callid;
  
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#getNewClientTransaction(javax2.sip.message.Request)
       */
      public ClientTransaction getNewClientTransaction(Request request)
              throws TransactionUnavailableException {
          if (request == null)
              throw new NullPointerException("null request");
          if (!sipStack.isAlive())
              throw new TransactionUnavailableException("Stack is stopped");
  
          SIPRequest sipRequest = (SIPRequest) request;
          if (sipRequest.getTransaction() != null)
              throw new TransactionUnavailableException(
                      "Transaction already assigned to request");
          if ( sipRequest.getMethod().equals(Request.ACK)) {
              throw new TransactionUnavailableException ("Cannot create client transaction for  " + Request.ACK);
          }
          // Be kind and assign a via header for this provider if the user is
          // sloppy
          if (sipRequest.getTopmostVia() == null) {
              ListeningPointImpl lp = (ListeningPointImpl) this
                      .getListeningPoint("udp");
              Via via = lp.getViaHeader();
              request.setHeader(via);
          }
          // Give the request a quick check to see if all headers are assigned.
          try {
              sipRequest.checkHeaders();
          } catch (ParseException ex) {
              throw new TransactionUnavailableException(ex.getMessage(), ex);
          }
  
          /*
           * User decided to give us his own via header branch. Lets see if it
           * results in a clash. If so reject the request.
           */
          if (sipRequest.getTopmostVia().getBranch() != null
                  && sipRequest.getTopmostVia().getBranch().startsWith(
                          SIPConstants.BRANCH_MAGIC_COOKIE)
                  && sipStack.findTransaction((SIPRequest) request, false) != null) {
              throw new TransactionUnavailableException(
                      "Transaction already exists!");
          }
  
  
  
  
          if (request.getMethod().equalsIgnoreCase(Request.CANCEL)) {
              SIPClientTransaction ct = (SIPClientTransaction) sipStack
                      .findCancelTransaction((SIPRequest) request, false);
              if (ct != null) {
                  ClientTransaction retval = sipStack.createClientTransaction(
                          (SIPRequest) request, ct.getMessageChannel());
  
                  ((SIPTransaction) retval).addEventListener(this);
                  sipStack.addTransaction((SIPClientTransaction) retval);
                  if (ct.getDialog() != null) {
                      ((SIPClientTransaction) retval).setDialog((SIPDialog) ct
                              .getDialog(), sipRequest.getDialogId(false));
  
                  }
                  return retval;
              }
  
          }
          if (sipStack.isLoggingEnabled())
              sipStack.getStackLogger().logDebug(
                      "could not find existing transaction for "
                              + ((SIPRequest) request).getFirstLine()
                              + " creating a new one ");
  
          // Could not find a dialog or the route is not set in dialog.
  
          Hop hop = null;
          try {
              hop = sipStack.getNextHop((SIPRequest) request);
              if (hop == null)
                  throw new TransactionUnavailableException(
                          "Cannot resolve next hop -- transaction unavailable");
          } catch (SipException ex) {
              throw new TransactionUnavailableException(
                      "Cannot resolve next hop -- transaction unavailable", ex);
          }
          String transport = hop.getTransport();
          ListeningPointImpl listeningPoint = (ListeningPointImpl) this
                  .getListeningPoint(transport);
  
          String dialogId = sipRequest.getDialogId(false);
          SIPDialog dialog = sipStack.getDialog(dialogId);
          if (dialog != null && dialog.getState() == DialogState.TERMINATED) {
  
              // throw new TransactionUnavailableException
              // ("Found a terminated dialog -- possible re-use of old tag
              // parameters");
              sipStack.removeDialog(dialog);
  
          }
  
          // An out of dialog route was found. Assign this to the
          // client transaction.
  
          try {
              // Set the brannch id before you ask for a tx.
              // If the user has set his own branch Id and the
              // branch id starts with a valid prefix, then take it.
              // otherwise, generate one. If branch ID checking has 
              // been requested, set the branch ID.
              String branchId = null;
              if (sipRequest.getTopmostVia().getBranch() == null
                      || !sipRequest.getTopmostVia().getBranch().startsWith(
                              SIPConstants.BRANCH_MAGIC_COOKIE)
                              || sipStack.checkBranchId() ) {
                  branchId = Utils.getInstance().generateBranchId();
  
                  sipRequest.getTopmostVia().setBranch(branchId);
              }
              Via topmostVia = sipRequest.getTopmostVia();
  
              //set port and transport if user hasn't already done this.
              if(topmostVia.getTransport() == null)
                  topmostVia.setTransport(transport);
  
              if(topmostVia.getPort() == -1)
                  topmostVia.setPort(listeningPoint.getPort());
              branchId = sipRequest.getTopmostVia().getBranch();
  
              SIPClientTransaction ct = (SIPClientTransaction) sipStack
                      .createMessageChannel(sipRequest, listeningPoint
                              .getMessageProcessor(), hop);
              if (ct == null)
                  throw new TransactionUnavailableException("Cound not create tx");
              ct.setNextHop(hop);
              ct.setOriginalRequest(sipRequest);
              ct.setBranch(branchId);
              // if the stack supports dialogs then
              if (sipStack.isDialogCreated(request.getMethod())) {
                  // create a new dialog to contain this transaction
                  // provided this is necessary.
                  // This could be a re-invite
                  // in which case the dialog is re-used.
                  // (but noticed by Brad Templeton)
                  if (dialog != null)
                      ct.setDialog(dialog, sipRequest.getDialogId(false));
                  else if (this.isAutomaticDialogSupportEnabled()) {
                      SIPDialog sipDialog = sipStack.createDialog(ct);
                      ct.setDialog(sipDialog, sipRequest.getDialogId(false));
                  }
              } else {
                  if (dialog != null) {
                      ct.setDialog(dialog, sipRequest.getDialogId(false));
                  }
  
              }
  
              // The provider is the event listener for all transactions.
              ct.addEventListener(this);
              return (ClientTransaction) ct;
          } catch (IOException ex) {
  
              throw new TransactionUnavailableException(
                      "Could not resolve next hop or listening point unavailable! ",
                      ex);
  
          } catch (java.text.ParseException ex) {
              InternalErrorHandler.handleException(ex);
              throw new TransactionUnavailableException(
                      "Unexpected Exception FIXME! ", ex);
          } catch (InvalidArgumentException ex) {
              InternalErrorHandler.handleException(ex);
              throw new TransactionUnavailableException(
                      "Unexpected Exception FIXME! ", ex);
          }
  
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#getNewServerTransaction(javax2.sip.message.Request)
       */
      public ServerTransaction getNewServerTransaction(Request request)
              throws TransactionAlreadyExistsException,
              TransactionUnavailableException {
  
          if (!sipStack.isAlive())
              throw new TransactionUnavailableException("Stack is stopped");
          SIPServerTransaction transaction = null;
          SIPRequest sipRequest = (SIPRequest) request;
          try {
              sipRequest.checkHeaders();
          } catch (ParseException ex) {
              throw new TransactionUnavailableException(ex.getMessage(), ex);
          }
  
          if ( request.getMethod().equals(Request.ACK)) {
              if ( sipStack.isLoggingEnabled())
                  sipStack.getStackLogger().logError("Creating server transaction for ACK -- makes no sense!");
              throw new TransactionUnavailableException("Cannot create Server transaction for ACK ");
          }
          /*
           * Got a notify.
           */
          if (sipRequest.getMethod().equals(Request.NOTIFY)
                  && sipRequest.getFromTag() != null
                  && sipRequest.getToTag() == null) {
  
              SIPClientTransaction ct = sipStack.findSubscribeTransaction(
                      sipRequest, (ListeningPointImpl) this.getListeningPoint());
              /* Issue 104 */
              if (ct == null && ! sipStack.deliverUnsolicitedNotify) {
                  throw new TransactionUnavailableException(
                          "Cannot find matching Subscription (and gov2.nist.javax2.sip.DELIVER_UNSOLICITED_NOTIFY not set)");
              }
          }
          if ( !sipStack.acquireSem()) {
              throw new TransactionUnavailableException(
              "Transaction not available -- could not acquire stack lock");
          }
          try {
              if (sipStack.isDialogCreated(sipRequest.getMethod())) {
                  if (sipStack.findTransaction((SIPRequest) request, true) != null)
                      throw new TransactionAlreadyExistsException(
                      "server transaction already exists!");
  
                  transaction = (SIPServerTransaction) ((SIPRequest) request)
                  .getTransaction();
                  if (transaction == null)
                      throw new TransactionUnavailableException(
                      "Transaction not available");
                  if (transaction.getOriginalRequest() == null)
                      transaction.setOriginalRequest(sipRequest);
                  try {
                      sipStack.addTransaction(transaction);
                  } catch (IOException ex) {
                      throw new TransactionUnavailableException(
                      "Error sending provisional response");
                  }
                  // So I can handle timeouts.
                  transaction.addEventListener(this);
                  if (isAutomaticDialogSupportEnabled()) {
                      // If automatic dialog support is enabled then
                      // this tx gets his own dialog.
                      String dialogId = sipRequest.getDialogId(true);
                      SIPDialog dialog = sipStack.getDialog(dialogId);
                      if (dialog == null) {
                          dialog = sipStack.createDialog(transaction);
  
                      }
                      transaction.setDialog(dialog, sipRequest.getDialogId(true));
                      if (sipRequest.getMethod().equals(Request.INVITE) && this.isDialogErrorsAutomaticallyHandled()) {
                          sipStack.putInMergeTable(transaction, sipRequest);
                      }
                      dialog.addRoute(sipRequest);
                      if (dialog.getRemoteTag() != null
                              && dialog.getLocalTag() != null) {
                          this.sipStack.putDialog(dialog);
                      }
                  }
  
              } else {
                  if (isAutomaticDialogSupportEnabled()) {
                      /*
                       * Under automatic dialog support, dialog is tied into a transaction. You cannot
                       * create a server tx except for dialog creating transactions. After that, all
                       * subsequent transactions are created for you by the stack.
                       */
                      transaction = (SIPServerTransaction) sipStack.findTransaction(
                              (SIPRequest) request, true);
                      if (transaction != null)
                          throw new TransactionAlreadyExistsException(
                          "Transaction exists! ");
                      transaction = (SIPServerTransaction) ((SIPRequest) request)
                      .getTransaction();
                      if (transaction == null)
                          throw new TransactionUnavailableException(
                          "Transaction not available!");
                      if (transaction.getOriginalRequest() == null)
                          transaction.setOriginalRequest(sipRequest);
                      // Map the transaction.
                      try {
                          sipStack.addTransaction(transaction);
                      } catch (IOException ex) {
                          throw new TransactionUnavailableException(
                          "Could not send back provisional response!");
                      }
  
                      // If there is a dialog already assigned then just update the
                      // dialog state.
                      String dialogId = sipRequest.getDialogId(true);
                      SIPDialog dialog = sipStack.getDialog(dialogId);
                      if (dialog != null) {
                          dialog.addTransaction(transaction);
                          dialog.addRoute(sipRequest);
                          transaction.setDialog(dialog, sipRequest.getDialogId(true));
                      }
  
                  } else {
                      transaction = (SIPServerTransaction) sipStack.findTransaction(
                              (SIPRequest) request, true);
                      if (transaction != null)
                          throw new TransactionAlreadyExistsException(
                          "Transaction exists! ");
                      transaction = (SIPServerTransaction) ((SIPRequest) request)
                      .getTransaction();
                      if (transaction != null) {
                          if (transaction.getOriginalRequest() == null)
                              transaction.setOriginalRequest(sipRequest);
                          // Map the transaction.
                          sipStack.mapTransaction(transaction);
  
                          // If there is a dialog already assigned then just
                          // assign the dialog to the transaction.
                          String dialogId = sipRequest.getDialogId(true);
                          SIPDialog dialog = sipStack.getDialog(dialogId);
                          if (dialog != null) {
                              dialog.addTransaction(transaction);
                              dialog.addRoute(sipRequest);
                              transaction.setDialog(dialog, sipRequest
                                      .getDialogId(true));
                          }
  
                          return transaction;
                      } else {
                          // tx does not exist so create the tx.
  
                          MessageChannel mc = (MessageChannel) sipRequest
                          .getMessageChannel();
                          transaction = sipStack.createServerTransaction(mc);
                          if (transaction == null)
                              throw new TransactionUnavailableException(
                              "Transaction unavailable -- too many servrer transactions");
  
                          transaction.setOriginalRequest(sipRequest);
                          sipStack.mapTransaction(transaction);
  
                          // If there is a dialog already assigned then just
                          // assign the dialog to the transaction.
                          String dialogId = sipRequest.getDialogId(true);
                          SIPDialog dialog = sipStack.getDialog(dialogId);
                          if (dialog != null) {
                              dialog.addTransaction(transaction);
                              dialog.addRoute(sipRequest);
                              transaction.setDialog(dialog, sipRequest
                                      .getDialogId(true));
                          }
  
                          return transaction;
                      }
                  }
  
              }
              return transaction;
          } finally {
              sipStack.releaseSem();
          }
  
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#getSipStack()
       */
      public SipStack getSipStack() {
          return (SipStack) this.sipStack;
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#removeSipListener(javax2.sip.SipListener)
       */
      public void removeSipListener(SipListener sipListener) {
          if (sipListener == this.getSipListener()) {
              this.sipListener = null;
          }
  
          boolean found = false;
  
          for (Iterator<SipProviderImpl> it = sipStack.getSipProviders(); it.hasNext();) {
              SipProviderImpl nextProvider = (SipProviderImpl) it.next();
              if (nextProvider.getSipListener() != null)
                  found = true;
          }
          if (!found) {
              sipStack.sipListener = null;
          }
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#sendRequest(javax2.sip.message.Request)
       */
      public void sendRequest(Request request) throws SipException {
          if (!sipStack.isAlive())
              throw new SipException("Stack is stopped.");
  
          // mranga: added check to ensure we are not sending empty (keepalive)
          // message.
          if (((SIPRequest) request).getRequestLine() != null
                  && request.getMethod().equals(Request.ACK)) {
              Dialog dialog = sipStack.getDialog(((SIPRequest) request)
                      .getDialogId(false));
              if (dialog != null && dialog.getState() != null) {
              	if (sipStack.isLoggingEnabled())
              		sipStack.getStackLogger().logWarning(
                          "Dialog exists -- you may want to use Dialog.sendAck() "
                                  + dialog.getState());
              }
          }
          Hop hop = sipStack.getRouter((SIPRequest) request).getNextHop(request);
          if (hop == null)
              throw new SipException("could not determine next hop!");
          SIPRequest sipRequest = (SIPRequest) request;
          // Check if we have a valid via.
          // Null request is used to send default proxy keepalive messages.
          if ((!sipRequest.isNullRequest()) && sipRequest.getTopmostVia() == null)
              throw new SipException("Invalid SipRequest -- no via header!");
  
          try {
              /*
               * JvB: Via branch should already be OK, dont touch it here? Some
               * apps forward statelessly, and then it's not set. So set only when
               * not set already, dont overwrite CANCEL branch here..
               */
              if (!sipRequest.isNullRequest()) {
                  Via via = sipRequest.getTopmostVia();
                  String branch = via.getBranch();
                  if (branch == null || branch.length() == 0) {
                      via.setBranch(sipRequest.getTransactionId());
                  }
              }
              MessageChannel messageChannel = null;
              if (this.listeningPoints.containsKey(hop.getTransport()
                      .toUpperCase()))
                  messageChannel = sipStack.createRawMessageChannel(
                          this.getListeningPoint(hop.getTransport()).getIPAddress(),
                          this.getListeningPoint(hop.getTransport()).getPort(), hop);
              if (messageChannel != null) {
                  messageChannel.sendMessage((SIPMessage) sipRequest,hop);
              } else {
                  throw new SipException(
                          "Could not create a message channel for "
                                  + hop.toString());
              }
          } catch (IOException ex) {
              if (sipStack.isLoggingEnabled()) {
                  sipStack.getStackLogger().logException(ex);
              }
  
              throw new SipException(
                      "IO Exception occured while Sending Request", ex);
  
          } catch (ParseException ex1) {
              InternalErrorHandler.handleException(ex1);
          } finally {
              if (sipStack.isLoggingEnabled())
                  sipStack.getStackLogger().logDebug(
                          "done sending " + request.getMethod() + " to hop "
                                  + hop);
          }
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#sendResponse(javax2.sip.message.Response)
       */
      public void sendResponse(Response response) throws SipException {
          if (!sipStack.isAlive())
              throw new SipException("Stack is stopped");
          SIPResponse sipResponse = (SIPResponse) response;
          Via via = sipResponse.getTopmostVia();
          if (via == null)
              throw new SipException("No via header in response!");
          SIPServerTransaction st = (SIPServerTransaction) sipStack.findTransaction((SIPMessage)response, true);
          if ( st != null   && st.getState() != TransactionState.TERMINATED && this.isAutomaticDialogSupportEnabled()) {
              throw new SipException("Transaction exists -- cannot send response statelessly");
          }
          String transport = via.getTransport();
  
          // check to see if Via has "received paramaeter". If so
          // set the host to the via parameter. Else set it to the
          // Via host.
          String host = via.getReceived();
  
          if (host == null)
              host = via.getHost();
  
          // Symmetric nat support
          int port = via.getRPort();
          if (port == -1) {
              port = via.getPort();
              if (port == -1) {
                  if (transport.equalsIgnoreCase("TLS"))
                      port = 5061;
                  else
                      port = 5060;
              }
          }
  
          // for correct management of IPv6 addresses.
          if (host.indexOf(":") > 0)
              if (host.indexOf("[") < 0)
                  host = "[" + host + "]";
  
          Hop hop = sipStack.getAddressResolver().resolveAddress(
                  new HopImpl(host, port, transport));
  
          try {
              ListeningPointImpl listeningPoint = (ListeningPointImpl) this
                      .getListeningPoint(transport);
              if (listeningPoint == null)
                  throw new SipException(
                          "whoopsa daisy! no listening point found for transport "
                                  + transport);
              MessageChannel messageChannel = sipStack.createRawMessageChannel(
                      this.getListeningPoint(hop.getTransport()).getIPAddress(),
                      listeningPoint.port, hop);
              messageChannel.sendMessage(sipResponse);
          } catch (IOException ex) {
              throw new SipException(ex.getMessage());
          }
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#setListeningPoint(javax2.sip.ListeningPoint)
       */
      public synchronized void setListeningPoint(ListeningPoint listeningPoint) {
          if (listeningPoint == null)
              throw new NullPointerException("Null listening point");
          ListeningPointImpl lp = (ListeningPointImpl) listeningPoint;
          lp.sipProvider = this;
          String transport = lp.getTransport().toUpperCase();
          this.address = listeningPoint.getIPAddress();
          this.port = listeningPoint.getPort();
          // This is the first listening point.
          this.listeningPoints.clear();
          this.listeningPoints.put(transport, listeningPoint);
  
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#getNewDialog(javax2.sip.Transaction)
       */
  
      public Dialog getNewDialog(Transaction transaction) throws SipException {
          if (transaction == null)
              throw new NullPointerException("Null transaction!");
  
          if (!sipStack.isAlive())
              throw new SipException("Stack is stopped.");
  
          if (isAutomaticDialogSupportEnabled())
              throw new SipException(" Error - AUTOMATIC_DIALOG_SUPPORT is on");
  
          if (!sipStack.isDialogCreated(transaction.getRequest().getMethod()))
              throw new SipException("Dialog cannot be created for this method "
                      + transaction.getRequest().getMethod());
  
          SIPDialog dialog = null;
          SIPTransaction sipTransaction = (SIPTransaction) transaction;
  
          if (transaction instanceof ServerTransaction) {
              SIPServerTransaction st = (SIPServerTransaction) transaction;
              Response response = st.getLastResponse();
              if (response != null) {
                  if (response.getStatusCode() != 100)
                      throw new SipException(
                              "Cannot set dialog after response has been sent");
              }
              SIPRequest sipRequest = (SIPRequest) transaction.getRequest();
              String dialogId = sipRequest.getDialogId(true);
              dialog = sipStack.getDialog(dialogId);
              if (dialog == null) {
                  dialog = sipStack.createDialog((SIPTransaction) transaction);
                  // create and register the dialog and add the inital route set.
                  dialog.addTransaction(sipTransaction);
                  dialog.addRoute(sipRequest);
                  sipTransaction.setDialog(dialog, null);
  
              } else {
                  sipTransaction.setDialog(dialog, sipRequest.getDialogId(true));
              }
              if (sipRequest.getMethod().equals(Request.INVITE) && this.isDialogErrorsAutomaticallyHandled()) {
                  sipStack.putInMergeTable(st, sipRequest);
              }
          } else {
  
              SIPClientTransaction sipClientTx = (SIPClientTransaction) transaction;
  
              SIPResponse response = sipClientTx.getLastResponse();
  
              if (response == null) {
                  // A response has not yet been received, then set this up as the
                  // default dialog.
                  SIPRequest request = (SIPRequest) sipClientTx.getRequest();
  
                  String dialogId = request.getDialogId(false);
                  dialog = sipStack.getDialog(dialogId);
                  if (dialog != null) {
                      throw new SipException("Dialog already exists!");
                  } else {
                      dialog = sipStack.createDialog(sipTransaction);
                  }
                  sipClientTx.setDialog(dialog, null);
  
              } else {
                  throw new SipException(
                          "Cannot call this method after response is received!");
              }
          }
          dialog.addEventListener(this);
          return dialog;
  
      }
  
      /**
       * Invoked when an error has ocurred with a transaction. Propagate up to the
       * listeners.
       *
       * @param transactionErrorEvent
       *            Error event.
       */
      public void transactionErrorEvent(
              SIPTransactionErrorEvent transactionErrorEvent) {
          SIPTransaction transaction = (SIPTransaction) transactionErrorEvent
                  .getSource();
  
          if (transactionErrorEvent.getErrorID() == SIPTransactionErrorEvent.TRANSPORT_ERROR) {
              // There must be a way to inform the TU here!!
              if (sipStack.isLoggingEnabled()) {
                  sipStack.getStackLogger().logDebug(
                          "TransportError occured on " + transaction);
              }
              // Treat this like a timeout event. (Suggestion from Christophe).
              Object errorObject = transactionErrorEvent.getSource();
              Timeout timeout = Timeout.TRANSACTION;
              TimeoutEvent ev = null;
  
              if (errorObject instanceof SIPServerTransaction) {
                  ev = new TimeoutEvent(this, (ServerTransaction) errorObject,
                          timeout);
              } else {
                  SIPClientTransaction clientTx = (SIPClientTransaction) errorObject;
                  Hop hop = clientTx.getNextHop();
                  if ( sipStack.getRouter() instanceof RouterExt ) {
                      ((RouterExt) sipStack.getRouter()).transactionTimeout(hop);
                  }
                  ev = new TimeoutEvent(this, (ClientTransaction) errorObject,
                          timeout);
              }
              // Handling transport error like timeout
              this.handleEvent(ev, (SIPTransaction) errorObject);
          } else if (transactionErrorEvent.getErrorID() == SIPTransactionErrorEvent.TIMEOUT_ERROR) {
              // This is a timeout event.
              Object errorObject = transactionErrorEvent.getSource();
              Timeout timeout = Timeout.TRANSACTION;
              TimeoutEvent ev = null;
  
              if (errorObject instanceof SIPServerTransaction) {
                  ev = new TimeoutEvent(this, (ServerTransaction) errorObject,
                          timeout);
              } else {
                  SIPClientTransaction clientTx = (SIPClientTransaction) errorObject;
                  Hop hop = clientTx.getNextHop();
                  if ( sipStack.getRouter() instanceof RouterExt ) {
                      ((RouterExt) sipStack.getRouter()).transactionTimeout(hop);
                  }
  
                  ev = new TimeoutEvent(this, (ClientTransaction) errorObject,
                          timeout);
              }
              this.handleEvent(ev, (SIPTransaction) errorObject);
  
          } else if (transactionErrorEvent.getErrorID() == SIPTransactionErrorEvent.TIMEOUT_RETRANSMIT) {
              // This is a timeout retransmit event.
              // We should never get this if retransmit filter is
              // enabled (ie. in that case the stack should handle.
              // all retransmits.
              Object errorObject = transactionErrorEvent.getSource();
              Transaction tx = (Transaction) errorObject;
  
              if (tx.getDialog() != null)
                  InternalErrorHandler.handleException("Unexpected event !",
                          this.sipStack.getStackLogger());
  
              Timeout timeout = Timeout.RETRANSMIT;
              TimeoutEvent ev = null;
  
              if (errorObject instanceof SIPServerTransaction) {
                  ev = new TimeoutEvent(this, (ServerTransaction) errorObject,
                          timeout);
              } else {
                  ev = new TimeoutEvent(this, (ClientTransaction) errorObject,
                          timeout);
              }
              this.handleEvent(ev, (SIPTransaction) errorObject);
          }
      }
      
      /*
       * (non-Javadoc)
       * @see gov2.nist.javax2.sip.stack.SIPDialogEventListener#dialogErrorEvent(gov2.nist.javax2.sip.stack.SIPDialogErrorEvent)
       */
      public synchronized void dialogErrorEvent(SIPDialogErrorEvent dialogErrorEvent) {
          SIPDialog sipDialog = (SIPDialog) dialogErrorEvent.getSource();
          Reason reason = Reason.AckNotReceived;
          if (dialogErrorEvent.getErrorID() == SIPDialogErrorEvent.DIALOG_ACK_NOT_SENT_TIMEOUT) {
          	reason= Reason.AckNotSent;
          } else if (dialogErrorEvent.getErrorID() == SIPDialogErrorEvent.DIALOG_REINVITE_TIMEOUT) {
              reason = Reason.ReInviteTimeout;
          }
          if (sipStack.isLoggingEnabled()) {
              sipStack.getStackLogger().logDebug(
                      "Dialog TimeoutError occured on " + sipDialog);
          }
          DialogTimeoutEvent ev = new DialogTimeoutEvent(this, sipDialog, reason);
          // Handling transport error like timeout
          this.handleEvent(ev, null);
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#getListeningPoints()
       */
      public synchronized ListeningPoint[] getListeningPoints() {
  
          ListeningPoint[] retval = new ListeningPointImpl[this.listeningPoints
                  .size()];
          this.listeningPoints.values().toArray(retval);
          return retval;
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#addListeningPoint(javax2.sip.ListeningPoint)
       */
      public synchronized void addListeningPoint(ListeningPoint listeningPoint)
              throws ObjectInUseException {
          ListeningPointImpl lp = (ListeningPointImpl) listeningPoint;
          if (lp.sipProvider != null && lp.sipProvider != this)
              throw new ObjectInUseException(
                      "Listening point assigned to another provider");
          String transport = lp.getTransport().toUpperCase();
          if (this.listeningPoints.isEmpty()) {
              // first one -- record the IP address/port of the LP
  
              this.address = listeningPoint.getIPAddress();
              this.port = listeningPoint.getPort();
          } else {
              if ((!this.address.equals(listeningPoint.getIPAddress()))
                      || this.port != listeningPoint.getPort())
                  throw new ObjectInUseException(
                          "Provider already has different IP Address associated");
  
          }
          if (this.listeningPoints.containsKey(transport)
                  && this.listeningPoints.get(transport) != listeningPoint)
              throw new ObjectInUseException(
                      "Listening point already assigned for transport!");
  
          // This is for backwards compatibility.
          lp.sipProvider = this;
  
          this.listeningPoints.put(transport, lp);
  
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#removeListeningPoint(javax2.sip.ListeningPoint)
       */
      public synchronized void removeListeningPoint(ListeningPoint listeningPoint)
              throws ObjectInUseException {
          ListeningPointImpl lp = (ListeningPointImpl) listeningPoint;
          if (lp.messageProcessor.inUse())
              throw new ObjectInUseException("Object is in use");
          this.listeningPoints.remove(lp.getTransport().toUpperCase());
  
      }
  
      /**
       * Remove all the listening points for this sip provider. This is called
       * when the stack removes the Provider
       */
      public synchronized void removeListeningPoints() {
          for (Iterator it = this.listeningPoints.values().iterator(); it
                  .hasNext();) {
              ListeningPointImpl lp = (ListeningPointImpl) it.next();
              lp.messageProcessor.stop();
              it.remove();
          }
  
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.SipProvider#setAutomaticDialogSupportEnabled(boolean)
       */
      public void setAutomaticDialogSupportEnabled(
              boolean automaticDialogSupportEnabled) {
          this.automaticDialogSupportEnabled = automaticDialogSupportEnabled;
          if ( this.automaticDialogSupportEnabled ) {
              this.dialogErrorsAutomaticallyHandled = true;
          }
      }
  
      /**
       * @return Returns the automaticDialogSupportEnabled.
       */
      public boolean isAutomaticDialogSupportEnabled() {
          return automaticDialogSupportEnabled;
      }
  
      /*
       * (non-Javadoc)
       * @see gov2.nist.javax2.sip.SipProviderExt#setDialogErrorsAutomaticallyHandled()
       */
      public void setDialogErrorsAutomaticallyHandled() {
          this.dialogErrorsAutomaticallyHandled = true;
      }
      
      public boolean isDialogErrorsAutomaticallyHandled() {
          return this.dialogErrorsAutomaticallyHandled;
      }
  
  
      /**
       * @return the sipListener
       */
      public SipListener getSipListener() {
          return sipListener;
      }
  
     
  }
  