Package org.zoolu.sip.provider

Source Code of org.zoolu.sip.provider.SipProvider

/*
* Copyright (C) 2005 Luca Veltri - University of Parma - Italy
*
* This file is part of MjSip (http://www.mjsip.org)
*
* MjSip is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MjSip is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MjSip; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Author(s):
* Luca Veltri (luca.veltri@unipr.it)
*/

/*
* Greg Dorfuss - http://mhspot.com
* Allow Invite Listener to be added no matter what
* Added SIP OPTION message support
* Added 405 to NOTIFY messages
* added extra break for branch - I was getting intermittant duplicate branches
* added params to control handling of addresses
* 2008/10/08 added 302 redir if busy
* added sychronize on invite request
*/


package org.zoolu.sip.provider;


import org.zoolu.net.*;
import org.zoolu.sip.header.*;
import org.zoolu.sip.message.Message;
import org.zoolu.sip.message.MessageFactory;
import org.zoolu.sip.message.SipResponses;
import org.zoolu.sip.address.*;
import org.zoolu.sip.transaction.TransactionServer;
import org.zoolu.sip.transaction.InviteTransactionServer;
import org.zoolu.sip.transaction.Transaction;
import org.zoolu.tools.Configure;
import org.zoolu.tools.Configurable;
import org.zoolu.tools.Parser;
import org.zoolu.tools.Random;
import org.zoolu.tools.Log;
import org.zoolu.tools.LogLevel;
import org.zoolu.tools.RotatingLog;
//import org.zoolu.tools.MD5;
import org.zoolu.tools.SimpleDigest;
import org.zoolu.tools.DateFormat;

import java.util.Hashtable;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
//PersonalJava
//import java.util.HashSet;
//import java.util.Iterator;
import org.zoolu.tools.HashSet;
import org.zoolu.tools.Iterator;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Date;


/** SipProvider implements the SIP transport layer, that is the layer responsable for
  * sending and receiving SIP messages. Messages are received by the callback function
  * defined in the interface SipProviderListener.
  * <p>
  * SipProvider implements also multiplexing/demultiplexing service through the use of
  * SIP interface identifiers and <i>onReceivedMessage()<i/> callback function
  * of specific SipProviderListener.
  * <p>
  * A SipProviderListener can be added to a SipProvider through the
  * addSipProviderListener(id,listener) method, where:
  * <b> - <i>id<i/> is the SIP interface identifier the listener has to be bound to,
  * <b> - <i>listener<i/> is the SipProviderListener that received messages are passed to.
  * <p/>
  * The SIP interface identifier specifies the type of messages the listener is going to
  * receive for. Together with the specific SipProvider, it represents the complete SIP
  * Service Access Point (SAP) address/identifier used for demultiplexing SIP messages
  * at receiving side.
  * <p/>
  * The identifier can be of one of the three following types: transaction_id, dialog_id,
  * or method_id. These types of identifiers characterize respectively:
  * <br> - messages within a specific transaction,
  * <br> - messages within a specific dialog,
  * <br> - messages related to a specific SIP method.
  * It is also possible to use the the identifier ANY to specify
  * <br> - all messages that are out of any transactions, dialogs, or already specified
  *        method types.
  * <p>
  * When receiving a message, the SipProvider first tries to look for a matching
  * transaction, then looks for a matching dialog, then for a matching method type,
  * and finally for a default listener (i.e. that with identifier ANY).
  * For the matched SipProviderListener, the method  <i>onReceivedMessage()</i> is fired.
  * <p>
  * Note: no 482 (Loop Detected) responses are generated for requests that does not
  * properly match any ongoing transactions, dialogs, nor method types.
  */
public class SipProvider implements Configurable, TransportListener, TcpServerListener
{

   // **************************** Constants ****************************

   /** UDP protocol type */
   public static final String PROTO_UDP = "udp";
   /** TCP protocol type */
   public static final String PROTO_TCP = "tcp";
   /** TLS protocol type */
   public static final String PROTO_TLS = "tls";
   /** SCTP protocol type */
   public static final String PROTO_SCTP = "sctp";

   /** String value "auto-configuration" used for auto configuration of the host address. */
   public static final String AUTO_CONFIGURATION = "AUTO-CONFIGURATION";

   /** String value "auto-configuration" used for auto configuration of the host address. */
   public static final String ALL_INTERFACES = "ALL-INTERFACES";

   /** String value "NO-OUTBOUND" used for setting no outbound proxy. */
   //public static final String NO_OUTBOUND="NO-OUTBOUND";

   /** Identifier used as listener id for capturing ANY incoming messages
     * that does not match any active method_id, transaction_id, nor dialog_id.
     * <br> In this context, "active" means that there is a active listener
     * for that specific method, transaction, or dialog. */
   public static final Identifier ANY = new Identifier("ANY");

   /** Identifier used as listener id for capturing any incoming messages in PROMISQUE mode,
     * that means that messages are passed to the present listener regardless of
     * any other active SipProviderListeners for specific messages.
     * <p/>
     * More than one SipProviderListener can be added and be active concurrently
     * for capturing messages in PROMISQUE mode. */
   public static final Identifier PROMISQUE = new Identifier("PROMISQUE");

   public static final Identifier INVITE = new Identifier("INVITE");

   /** Minimum length for a valid SIP message.  */
   private static final int MIN_MESSAGE_LENGTH = 12;

   // ***************** Readable/configurable attributes *****************
    String sipBusyUrl = null;
   /** Via address/name.
     * Use 'auto-configuration' for auto detection, or let it undefined. */
   String via_addr = null;

   /** Local SIP port */
   int host_port = 0;

   /** Network interface (IP address) used by SIP.
     * Use 'ALL-INTERFACES' for binding SIP to all interfaces (or let it undefined). */
   String host_ifaddr = null;

   /** Transport protocols (the first protocol is used as default) */
   String[] transport_protocols = null;

   /** Max number of (contemporary) open connections */
   int nmax_connections = 0;

   /** Outbound proxy (host_addr[:host_port]).
     * Use 'NONE' for not using an outbound proxy (or let it undefined). */
   SocketAddress outbound_proxy = null;

   /** Whether logging all packets (including non-SIP keepalive tokens). */
   boolean log_all_packets = false;

   private String inviteLock = "";

      // new params to control address handling
      private boolean sendResponseUsingOutboundProxy = false;
      private boolean useViaReceived = true;
      private boolean useViaRport = true;

   // for backward compatibility:

   /** Outbound proxy addr (for backward compatibility). */
   private String outbound_addr = null;
   /** Outbound proxy port (for backward compatibility). */
   private int outbound_port = -1;

   private OptionHandler optionHandler = null;


   // ********************* Non-readable attributes *********************

   /** Event Loger */
   protected Log event_log = null;

   /** Message Loger */
   protected Log message_log = null;

   /** Network interface (IP address) used by SIP. */
   IpAddress host_ipaddr = null;

   /** Default transport */
   String default_transport = null;

    static long breaker = 0;

   private boolean initComplete = false;




   /** Whether using UDP as transport protocol */
   boolean transport_udp = false;
   /** Whether using TCP as transport protocol */
   boolean transport_tcp = false;
   /** Whether using TLS as transport protocol */
   boolean transport_tls = false;
   /** Whether using SCTP as transport protocol */
   boolean transport_sctp = false;

   /** Whether adding 'rport' parameter on outgoing requests. */
   boolean rport=true;

   /** Whether forcing 'rport' parameter on incoming requests ('force-rport' mode). */
   boolean force_rport=false;

   /** List of provider listeners */
   SpcHashtable listeners=null;



   /** List of exception listeners */
   HashSet exception_listeners=null;

   /** UDP transport */
   UdpTransport udp=null;

   /** Tcp server */
   TcpServer tcp_server=null;

   /** Connections */
   Hashtable connections=null;


   // *************************** Costructors ***************************

   /** Creates a void SipProvider. */
   /*protected SipProvider()
   {
   }*/

   /** Creates a new SipProvider. */
   public SipProvider(String via_addr, int port)
   { 
     init(via_addr,port,null,null);
     initlog();
     startTrasport();
   }


   /** Creates a new SipProvider.
     * Costructs the SipProvider, initializing the SipProviderListeners, the transport protocols, and other attributes. */
   public SipProvider(String via_addr, int port, String[] protocols, String ifaddr)
   { 
     init(via_addr,port,protocols,ifaddr);
     initlog();
     startTrasport();
   }


   /** Creates a new SipProvider.
     * The SipProvider attributres are read from file. */
   public SipProvider(String file)
   { 
     if (!SipStack.isInit()) SipStack.init(file);
     new Configure(this,file);
     init(via_addr,host_port,transport_protocols,host_ifaddr);
     initlog();
     startTrasport();
   }


   /** Inits the SipProvider, initializing the SipProviderListeners, the transport protocols, the outbound proxy, and other attributes. */
   private void init(String viaddr, int port, String[] protocols, String ifaddr)
   { 
     if (!SipStack.isInit()) SipStack.init();
     via_addr = viaddr;
     if (via_addr == null || via_addr.equalsIgnoreCase(AUTO_CONFIGURATION)) via_addr = IpAddress.getLocalHostAddress().toString();
     host_port = port;
     if (host_port <= 0) host_port = SipStack.default_port;
     host_ipaddr = null;
     if (ifaddr != null && !ifaddr.equalsIgnoreCase(ALL_INTERFACES)) { 
       try
         host_ipaddr = IpAddress.getByName(ifaddr)
       } catch (IOException e) { 
         e.printStackTrace();
         host_ipaddr = null
       }
      }
    
      transport_protocols = protocols;
     
      if (transport_protocols==null) transport_protocols=SipStack.default_transport_protocols;
      default_transport=transport_protocols[0];
      for (int i=0; i<transport_protocols.length; i++)
      {  transport_protocols[i]=transport_protocols[i].toLowerCase();
         if (transport_protocols[i].equals(PROTO_UDP)) transport_udp=true;
         else
         if (transport_protocols[i].equals(PROTO_TCP)) transport_tcp=true;
         /*
         else
         if (transport_protocols[i].equals(PROTO_TLS)) transport_tls=true;
         else
         if (transport_protocols[i].equals(PROTO_SCTP)) transport_sctp=true;
         */
      }
      if (nmax_connections<=0) nmax_connections=SipStack.default_nmax_connections;

      // just for backward compatibility..
      if (outbound_port<0) outbound_port=SipStack.default_port;
      if (outbound_addr!=null)
      {  if (outbound_addr.equalsIgnoreCase(Configure.NONE) || outbound_addr.equalsIgnoreCase("NO-OUTBOUND")) outbound_proxy=null;
         else outbound_proxy=new SocketAddress(outbound_addr,outbound_port);
      }

      rport=SipStack.use_rport;
      force_rport=SipStack.force_rport;

      exception_listeners=new HashSet();
      //listeners=new Hashtable();
      Hashtable dupeKeys=new Hashtable();
     dupeKeys.put(INVITE, "");
      listeners=new SpcHashtable(dupeKeys);
      connections=new Hashtable();
   }


   /** Inits logs. */
   private void initlog()
   {  if (SipStack.debug_level>0)
      {  String filename=SipStack.log_path+"//"+via_addr+"."+host_port;
         event_log=new RotatingLog(filename+"_events.log",null,SipStack.debug_level,SipStack.max_logsize*1024,SipStack.log_rotations,SipStack.rotation_scale,SipStack.rotation_time);
         message_log=new RotatingLog(filename+"_messages.log",null,SipStack.debug_level,SipStack.max_logsize*1024,SipStack.log_rotations,SipStack.rotation_scale,SipStack.rotation_time);
      }
      printLog("Date: "+DateFormat.formatHHMMSS(new Date()),LogLevel.HIGH);
      printLog("SipStack: "+SipStack.release,LogLevel.HIGH);
      printLog("new SipProvider(): "+toString(),LogLevel.HIGH);
   }


   /** Starts the transport services. */
   private void startTrasport()
   {
      // start udp
      if (transport_udp)
      {  try
         if (host_ipaddr==null) udp=new UdpTransport(host_port,this);
            else udp=new UdpTransport(host_port,host_ipaddr,this);
            printLog("udp is up",LogLevel.MEDIUM);
         }
         catch (Exception e)
         {  printException(e,LogLevel.HIGH);
         }
      }
      // start tcp
      if (transport_tcp)
      {  try
         if (host_ipaddr==null) tcp_server=new TcpServer(host_port,this);
            else tcp_server=new TcpServer(host_port,host_ipaddr,this);
            printLog("tcp is up",LogLevel.MEDIUM);
         }
         catch (Exception e)
         {  printException(e,LogLevel.HIGH);
         }
      }
      //printLog("transport is up",LogLevel.MEDIUM);
   }


   /** Stops the transport services. */
   private void stopTrasport()
   {
      // stop udp
      if (udp!=null)
      {  printLog("udp is going down",LogLevel.LOWER);
         udp.halt();
         udp=null;
      }
      // stop tcp
      if (tcp_server!=null)
      {  printLog("tcp is going down",LogLevel.LOWER);
         tcp_server.halt();
         tcp_server=null;
      }
      if (connections!=null)
      {  printLog("connections are going down",LogLevel.LOWER);
         for (Enumeration e=connections.elements(); e.hasMoreElements(); )
         {  ConnectedTransport c=(ConnectedTransport)e.nextElement();
            c.halt();
         }
         connections=null;
      }
   }

   /** Stops the SipProviders. */
   public void halt()
   {  printLog("halt: SipProvider is going down",LogLevel.MEDIUM);
      stopTrasport();
      //listeners=new Hashtable();
   listeners=new SpcHashtable(null);
   exception_listeners=new HashSet();
   }


   /** Parses a single line (loaded from the config file) */
   public void parseLine(String line)
   {  String attribute;
      Parser par;
      int index=line.indexOf("=");
      if (index>0) {  attribute=line.substring(0,index).trim(); par=new Parser(line,index+1)}
      else attribute=line; par=new Parser("")}
      char[] delim={' ',','};

      if (attribute.equals("via_addr")) {  via_addr=par.getString(); return}
      if (attribute.equals("host_port")) {  host_port=par.getInt(); return; }
      if (attribute.equals("host_ifaddr")) {  host_ifaddr=par.getString(); return}
      if (attribute.equals("transport_protocols")) {  transport_protocols=par.getWordArray(delim); return}
      if (attribute.equals("nmax_connections")) {  nmax_connections=par.getInt(); return}
      if (attribute.equals("outbound_proxy"))
      {  String soaddr=par.getString();
         if (soaddr==null || soaddr.length()==0 || soaddr.equalsIgnoreCase(Configure.NONE) || soaddr.equalsIgnoreCase("NO-OUTBOUND")) outbound_proxy=null;
         else outbound_proxy=new SocketAddress(soaddr);
         return;
      }
      if (attribute.equals("log_all_packets")) { log_all_packets=(par.getString().toLowerCase().startsWith("y")); return; }

      // old parameters
      if (attribute.equals("host_addr")) System.err.println("WARNING: parameter 'host_addr' is no more supported; use 'via_addr' instead.");
      if (attribute.equals("all_interfaces")) System.err.println("WARNING: parameter 'all_interfaces' is no more supported; use 'host_iaddr' for setting a specific interface or let it undefined.");
      if (attribute.equals("use_outbound")) System.err.println("WARNING: parameter 'use_outbound' is no more supported; use 'outbound_proxy' for setting an outbound proxy or let it undefined.");
      if (attribute.equals("outbound_addr"))
      {  System.err.println("WARNING: parameter 'outbound_addr' has been deprecated; use 'outbound_proxy=<host_addr>[:<host_port>]' instead.");
         outbound_addr=par.getString();
         return;
      }
      if (attribute.equals("outbound_port"))
      {  System.err.println("WARNING: parameter 'outbound_port' has been deprecated; use 'outbound_proxy=<host_addr>[:<host_port>]' instead.");
         outbound_port=par.getInt();
         return;
      }

      if (attribute.equals("sendResponseUsingOutboundProxy")) { sendResponseUsingOutboundProxy=(par.getString().toLowerCase().startsWith("y")); return; }
          if (attribute.equals("useViaReceived")) { useViaReceived=(par.getString().toLowerCase().startsWith("y")); return; }
          if (attribute.equals("useViaRport")) { useViaRport=(par.getString().toLowerCase().startsWith("y")); return; }

          if (attribute.equals("SipInboundAllChannelsBusyAction"))
          {
            String sipInboundAllChannelsBusyAction=(par.getRemainingString());
            if (sipInboundAllChannelsBusyAction.startsWith("transferto:"))  // redirect the call elsewhere
             sipBusyUrl=sipInboundAllChannelsBusyAction.replaceAll("transferto:","");
            else
              sipBusyUrl=null;
            return;
      }

   }


   /** Converts the entire object into lines (to be saved into the config file) */
   protected String toLines()
   {  // currently not implemented..
      return toString();
   }


   /** Gets a String with the list of transport protocols. */
   private String transportProtocolsToString()
   {  String list=transport_protocols[0];
      for (int i=1; i<transport_protocols.length; i++) list+="/"+transport_protocols[i];
      return list;
   }


   // ************************** Public methods *************************

   /** Gets via address. */
   public String getViaAddress()
   {  return via_addr;
   }

   /** Sets via address. */
   /*public void setViaAddress(String addr)
   {  via_addr=addr;
   }*/

   /** Gets host port. */
   public int getPort()
   {  return host_port;
   }

   /** Whether binding the sip provider to all interfaces or only on the specified host address. */
   public boolean isAllInterfaces()
   {  return host_ipaddr==null;
   }

   /** Gets host interface IpAddress. */
   public IpAddress getInterfaceAddress()
   {  return host_ipaddr;
   }

   /** Gets array of transport protocols. */
   public String[] getTransportProtocols()
   {  return transport_protocols;
   }

   /** Gets the default transport protocol. */
   public String getDefaultTransport()
   {  return default_transport;
   }

   /** Gets the default transport protocol. */
   public void setDefaultTransport(String proto)
   {  default_transport=proto;
   }

   /** Sets rport support. */
   public void setRport(boolean flag)
   {  rport=flag;
   }

   /** Whether using rport. */
   public boolean isRportSet()
   {  return rport;
   }

   /** Sets 'force-rport' mode. */
   public void setForceRport(boolean flag)
   {  force_rport=flag;
   }

   /** Whether using 'force-rport' mode. */
   public boolean isForceRportSet()
   {  return force_rport;
   }

   /** Whether has outbound proxy. */
   public boolean hasOutboundProxy()
   {  return outbound_proxy!=null;
   }

   /** Gets the outbound proxy. */
   public SocketAddress getOutboundProxy()
   {  return outbound_proxy;
   }

   /** Sets the outbound proxy. Use 'null' for not using any outbound proxy. */
   public void setOutboundProxy(SocketAddress soaddr)
   {  outbound_proxy=soaddr;
   }

   /** Removes the outbound proxy. */
   /*public void removeOutboundProxy()
   {  setOutboundProxy(null);
   }*/

   /** Gets the max number of (contemporary) open connections. */
   public int getNMaxConnections()
   {  return nmax_connections;
   }

   /** Sets the max number of (contemporary) open connections. */
   public void setNMaxConnections(int n)
   {  nmax_connections=n;
   }


   /** Gets event log. */
   public Log getLog()
   {  return event_log;
   }


   /** Returns the list (Hashtable) of active listener_IDs. */
   public SpcHashtable getListeners()
   {  return listeners;
   }


   /** Adds a new listener to the SipProvider for caputering any message in PROMISQUE mode.
     * It is the same as using method addSipProviderListener(SipProvider.PROMISQUE,listener).
     * <p/>
     * When capturing messages in promisque mode all messages are passed to the SipProviderListener
     * before passing them to the specific listener (if present).
     * <br/> Note that more that one SipProviderListener can be active in promisque mode
     * at the same time;in that case the same message is passed to all PROMISQUE
     * SipProviderListeners.
     * @param listener is the SipProviderListener.
     * @return It returns <i>true</i> if the SipProviderListener is added,
     * <i>false</i> if the listener_ID is already in use. */
   public boolean addSipProviderPromisqueListener(SipProviderListener listener)
   {  return addSipProviderListener(PROMISQUE,listener);
   }

   /** Adds a new listener to the SipProvider for caputering ANY message.
     * It is the same as using method addSipProviderListener(SipProvider.ANY,listener).
     * @param listener is the SipProviderListener.
     * @return It returns <i>true</i> if the SipProviderListener is added,
     * <i>false</i> if the listener_ID is already in use. */
   public boolean addSipProviderListener(SipProviderListener listener)
   {  return addSipProviderListener(ANY,listener);
   }

   /** Adds a new listener to the SipProvider.
     * @param id is the unique identifier for the messages which the listener
     * as to be associated to. It is used as key.
     * It can identify a method, a transaction, or a dialog.
     * Use SipProvider.ANY to capture all messages.
     * Use SipProvider.PROMISQUE if you want to capture all message in promisque mode
     * (letting other listeners to capture the same received messages).
     * @param listener is the SipProviderListener for this message id.
     * @return It returns <i>true</i> if the SipProviderListener is added,
     * <i>false</i> if the listener_ID is already in use. */
   public boolean addSipProviderListener(Identifier key, SipProviderListener listener)
   {  printLog("adding SipProviderListener: "+key,LogLevel.MEDIUM);
      boolean ret;
      //Identifier key=id;
      if (listeners.containsKey(key) && !key.toString().equals("INVITE"))
      {  printWarning("trying to add a SipProviderListener with a id that is already in use.",LogLevel.HIGH);
         ret=false;
      }
      else
      listeners.put(key,listener);
         ret=true;
      }

      if (listeners!=null)
      {  String list="";
         for (Enumeration e=listeners.keys(); e.hasMoreElements();) list+=e.nextElement()+", ";
         printLog(listeners.size()+" listeners: "+list,LogLevel.LOW);
      }
      return ret;
   }


   /** Removes a SipProviderListener.
     * @param id is the unique identifier used to select the listened messages.
     * @return It returns <i>true</i> if the SipProviderListener is removed,
     * <i>false</i> if the  identifier is missed. */
   public boolean removeSipProviderListener(Identifier key)
   {  printLog("removing SipProviderListener: "+key,LogLevel.MEDIUM);
      boolean ret;
      //Identifier key=id;
      if (!listeners.containsKey(key))
      {  printWarning("trying to remove a missed SipProviderListener.",LogLevel.HIGH);
         ret=false;
      }
      else
      listeners.remove(key);
         ret=true;
      }

      if (listeners!=null)
      {  String list="";
         for (Enumeration e=listeners.keys(); e.hasMoreElements();) list+=e.nextElement()+", ";
         printLog(listeners.size()+" listeners: "+list,LogLevel.LOW);
      }
      return ret;
   }




  public boolean removeSipProviderListener(Identifier key,Object listener)
     {  printLog("removing SipProviderListener Pair: "+key ,LogLevel.HIGH);
        boolean ret;

        if (!listeners.containsPair(key,listener))
        {  printLog("trying to remove a missed SipProviderListener Pair." ,LogLevel.LOW);
           ret=false;
        }
        else
        {
           listeners.removePair(key,listener);
             ret=true;
        }

        if (listeners!=null)
        {
         String list="";
           for (Enumeration e=listeners.keys(); e.hasMoreElements();) list+=e.nextElement()+", ";
           printLog(listeners.size()+" listeners: "+list+"\n"+listeners.toString() ,LogLevel.LOW);
        }
        return ret;
   }

   /** Sets the SipProviderExceptionListener.
     * The SipProviderExceptionListener is the listener for all exceptions
     * thrown by the SipProviders.
     * @param e_listener is the SipProviderExceptionListener.
     * @return It returns <i>true</i> if the SipProviderListener has been correctly set,
     * <i>false</i> if the SipProviderListener was already set. */
   public boolean addSipProviderExceptionListener(SipProviderExceptionListener e_listener)
   {  printLog("adding SipProviderExceptionListener",LogLevel.MEDIUM);
      if (exception_listeners.contains(e_listener))
      {  printWarning("trying to add an already present SipProviderExceptionListener.",LogLevel.HIGH);
         return false;
      }
      else
      exception_listeners.add(e_listener);
         return true;
      }
   }


   /** Removes a SipProviderExceptionListener.
     * @param e_listener is the SipProviderExceptionListener.
     * @return It returns <i>true</i> if the SipProviderExceptionListener has been correctly removed,
     * <i>false</i> if the SipProviderExceptionListener is missed. */
   public boolean removeSipProviderExceptionListener(SipProviderExceptionListener e_listener)
   {  printLog("removing SipProviderExceptionListener",LogLevel.MEDIUM);
      if (!exception_listeners.contains(e_listener))
      {  printWarning("trying to remove a missed SipProviderExceptionListener.",LogLevel.HIGH);
         return false;
      }
      else
      exception_listeners.remove(e_listener);
         return true;
      }
   }


   /** Sends a Message, specifing the transport portocol, nexthop address and port.
     * <p> This is a low level method and
     * forces the message to be routed to a specific nexthop address, port and transport,
     * regardless whatever the Via, Route, or request-uri, address to.
     * <p>
     * In case of connection-oriented transport, the connection is selected as follows:
     * <br> - if an existing connection is found matching the destination
     *        end point (socket), such connection is used, otherwise
     * <br> - a new connection is established
     *
     * @return It returns a Connection in case of connection-oriented delivery
     * (e.g. TCP) or null in case of connection-less delivery (e.g. UDP)
     */
   public ConnectionIdentifier sendMessage(Message msg, String proto, String dest_addr, int dest_port, int ttl)
   {  if (log_all_packets || msg.getLength()>MIN_MESSAGE_LENGTH) printLog("Resolving host address '"+dest_addr+"'",LogLevel.MEDIUM);
      try
      IpAddress dest_ipaddr=IpAddress.getByName(dest_addr);
         return sendMessage(msg,proto,dest_ipaddr,dest_port,ttl);
      }
      catch (Exception e)
      {  printException(e,LogLevel.HIGH);
         return null;
      }
   }

   /** Sends a Message, specifing the transport portocol, nexthop address and port. */
   private ConnectionIdentifier sendMessage(Message msg, String proto, IpAddress dest_ipaddr, int dest_port, int ttl)
   {  ConnectionIdentifier conn_id=new ConnectionIdentifier(proto,dest_ipaddr,dest_port);
      if (log_all_packets || msg.getLength()>MIN_MESSAGE_LENGTH) printLog("Sending message to "+conn_id,LogLevel.MEDIUM);

      if (transport_udp && proto.equals(PROTO_UDP))
      {  // UDP
         //printLog("using UDP",LogLevel.LOW);
         conn_id=null;
         try
         // if (ttl>0 && multicast_address) do something?
            udp.sendMessage(msg,dest_ipaddr,dest_port);
         }
         catch (IOException e)
         {  printException(e,LogLevel.HIGH);
            return null;
         }
      }
      else
      if (transport_tcp && proto.equals(PROTO_TCP))
      {  // TCP
         //printLog("using TCP",LogLevel.LOW);
         if (!connections.containsKey(conn_id))
         {  printLog("no active connection found matching "+conn_id,LogLevel.MEDIUM);
            printLog("open "+proto+" connection to "+dest_ipaddr+":"+dest_port,LogLevel.MEDIUM);
            TcpTransport conn=null;
            try
            conn=new TcpTransport(dest_ipaddr,dest_port,this);
            }
            catch (Exception e)
            {  printLog("connection setup FAILED",LogLevel.HIGH);
               return null;
            }
            printLog("connection "+conn+" opened",LogLevel.HIGH);
            addConnection(conn);
         }
         else
         printLog("active connection found matching "+conn_id,LogLevel.MEDIUM);
         }
         ConnectedTransport conn=(ConnectedTransport)connections.get(conn_id);
         if (conn!=null)
         {  printLog("sending data through conn "+conn,LogLevel.MEDIUM);
            try
            conn.sendMessage(msg);
               conn_id=new ConnectionIdentifier(conn);
            }
            catch (IOException e)
            {  printException(e,LogLevel.HIGH);
               return null;
            }
         }
         else
         // this point has not to be reached
            printLog("ERROR: conn "+conn_id+" not found: abort.",LogLevel.MEDIUM);
            return null;
         }
      }
      else
      // otherwise
         printWarning("Unsupported protocol ("+proto+"): Message discarded",LogLevel.HIGH);
         return null;
      }
      // logs
      String dest_addr=dest_ipaddr.toString();
      printMessageLog(proto,dest_addr,dest_port,msg.getLength(),msg,"sent");
      return conn_id;
   }


   /** Sends the message <i>msg</i>.
     * <p>
     * The destination for the request is computed as follows:
     * <br> - if <i>outbound_addr</i> is set, <i>outbound_addr</i> and
     *        <i>outbound_port</i> are used, otherwise
     * <br> - if message has Route header with lr option parameter (i.e. RFC3261 compliant),
     *        the first Route address is used, otherwise
     * <br> - the request's Request-URI is considered.
     * <p>
     * The destination for the response is computed based on the sent-by parameter in
     *     the Via header field (RFC3261 compliant)
     * <p>
     * As transport it is used the protocol specified in the 'via' header field
     * <p>
     * In case of connection-oriented transport:
     * <br> - if an already established connection is found matching the destination
     *        end point (socket), such connection is used, otherwise
     * <br> - a new connection is established
     *
     * @return Returns a ConnectionIdentifier in case of connection-oriented delivery
     * (e.g. TCP) or null in case of connection-less delivery (e.g. UDP)
     */
   public ConnectionIdentifier sendMessage(Message msg)
   {  printLog("Sending message:\r\n"+msg.toString(),LogLevel.LOWER);

      // select the transport protocol
      ViaHeader via=msg.getViaHeader();
      String proto=via.getProtocol().toLowerCase();
      printLog("using transport "+proto,LogLevel.MEDIUM);

      // select the destination address and port
      String dest_addr=null;
      int dest_port=0;
      int ttl=0;

      if (msg.isRequest())
      {  // REQUESTS
         if (outbound_proxy!=null)
         {  dest_addr=outbound_proxy.getAddress().toString();
            dest_port=outbound_proxy.getPort();
         }
         else
         if (msg.hasRouteHeader() && msg.getRouteHeader().getNameAddress().getAddress().hasLr())
            {
               SipURL url=msg.getRouteHeader().getNameAddress().getAddress();
               dest_addr=url.getHost();
               dest_port=url.getPort();
            }
            else
            SipURL url=msg.getRequestLine().getAddress();
               dest_addr=url.getHost();
               dest_port=url.getPort();
               if (url.hasMaddr())
               {  dest_addr=url.getMaddr();
                  if (url.hasTtl()) ttl=url.getTtl();
                  // update the via header by adding maddr and ttl params
                  via.setMaddr(dest_addr);
                  if (ttl>0) via.setTtl(ttl);
                  msg.removeViaHeader();
                  msg.addViaHeader(via);
               }
            }
         }
      }
      else
      // RESPONSES

        if (outbound_proxy!=null && this.sendResponseUsingOutboundProxy)
              {
               dest_addr=outbound_proxy.getAddress().toString();
                 dest_port=outbound_proxy.getPort();
              }
              else
          {
         SipURL url=via.getSipURL();
           if (via.hasReceived()) dest_addr=via.getReceived();
         else
           dest_addr=url.getHost();

         if (via.hasRport())
             dest_port=via.getRport();

         if (dest_port<=0)
           dest_port=url.getPort();
      }


}
      if (dest_port<=0) dest_port=SipStack.default_port;

      return sendMessage(msg,proto,dest_addr,dest_port,ttl);
   }



  /** Sends the message <i>msg</i> using the specified connection. */
   public ConnectionIdentifier sendMessage(Message msg, ConnectionIdentifier conn_id)
   {  if (log_all_packets || msg.getLength()>MIN_MESSAGE_LENGTH) printLog("Sending message through conn "+conn_id,LogLevel.HIGH);
      printLog("message:\r\n"+msg.toString(),LogLevel.LOWER);

      if (conn_id!=null && connections.containsKey(conn_id))
      {  // connection exists
         printLog("active connection found matching "+conn_id,LogLevel.MEDIUM);
         ConnectedTransport conn=(ConnectedTransport)connections.get(conn_id);
         try
         conn.sendMessage(msg);
            // logs
            //String proto=conn.getProtocol();
            String proto=conn.getProtocol();
            String dest_addr=conn.getRemoteAddress().toString();
            int dest_port=conn.getRemotePort();
            printMessageLog(proto,dest_addr,dest_port,msg.getLength(),msg,"sent");
            return conn_id;
         }
         catch (Exception e)
         {  printException(e,LogLevel.HIGH);
         }
      }
      //else
      printLog("no active connection found matching "+conn_id,LogLevel.MEDIUM);
      return sendMessage(msg);
   }


   /** Processes the message received.
     * It is called each time a new message is received by the transport layer, and
     * it performs the actual message processing. */
   protected void processReceivedMessage(Message msg)
   {  try
      // logs
         printMessageLog(msg.getTransportProtocol(),msg.getRemoteAddress(),msg.getRemotePort(),msg.getLength(),msg,"received");

         // discard too short messages
         if (msg.getLength()<=2)
         {  if (log_all_packets) printLog("message too short: discarded\r\n",LogLevel.LOW);
            return;
         }
         // discard non-SIP messages
         String first_line=msg.getFirstLine();
         if (first_line==null || first_line.toUpperCase().indexOf("SIP/2.0")<0)
         {  if (log_all_packets) printLog("NOT a SIP message: discarded\r\n",LogLevel.LOW);
            return;
         }
         printLog("received new SIP message",LogLevel.HIGH);
         printLog("message:\r\n"+msg.toString(),LogLevel.LOWER);

         // if a request, handle "received" and "rport" parameters
         if (msg.isRequest())
         {  ViaHeader vh=msg.getViaHeader();
            boolean via_changed=false;
            String src_addr=msg.getRemoteAddress();
            int src_port=msg.getRemotePort();
            String via_addr=vh.getHost();
            int via_port=vh.getPort();
            if (via_port<=0) via_port=SipStack.default_port;

            if (!via_addr.equals(src_addr) && this.useViaReceived)
            {  vh.setReceived(src_addr);
               via_changed=true;
            }

            if (this.useViaRport)
          {
            if (vh.hasRport())
            {
         vh.setRport(src_port);
               via_changed=true;
            }
            else
            if (force_rport && via_port!=src_port)
               {  vh.setRport(src_port);
                  via_changed=true;
               }
            }
     }


           if (via_changed)
            {  msg.removeViaHeader();
               msg.addViaHeader(vh);
            }
         }

         // is there any listeners?
         if (listeners==null || listeners.size()==0)
         {
       if (initComplete)
       printLog("no listener found: meesage discarded.",LogLevel.HIGH);
            return;
         }

         // try to look for a UA in promisque mode
         if (listeners.containsKey(PROMISQUE))
         {  printLog("message passed to uas: "+PROMISQUE,LogLevel.MEDIUM);
            ((SipProviderListener)listeners.get(PROMISQUE)).onReceivedMessage(this,msg);
         }

         // after the callback check if the message is still valid
         if (!msg.isRequest() && !msg.isResponse())
         {  printLog("No valid SIP message: message discarded.",LogLevel.HIGH);
            return;
         }

         // this was the promisque listener; now keep on looking for a tighter listener..


          if (msg.isRequest() && msg.isOption())
              {
                // handle SIP OPTIONS message
                int optStat=486; // default busy
                if (listeners.containsKey(INVITE)) // if existing INVITE key then we might be available
                {
                      if (this.optionHandler!=null)
                      {
                        String optBody=optionHandler.onOptionMsgReceived();
                        if (optBody!=null)
                        {
                          optStat=200;
                          // add capabilities to msg to be sent
                         printLog("OPTION Request - response status="+optStat , LogLevel.HIGH);
                         TransactionServer ts=new TransactionServer(this,msg,null);
                        ts.respondWith(MessageFactory.createResponse(msg,optStat,SipResponses.reasonOf(optStat),null,null,"application/sdp",optBody));
                      return;
                        }
                      }
                      else
                      optStat=200; // assume available if no option handler
                }

                 printLog("OPTION Request - response status="+optStat,LogLevel.MEDIUM );
                 TransactionServer ts=new TransactionServer(this,msg,null);
                   ts.respondWith(MessageFactory.createResponse(msg,optStat,SipResponses.reasonOf(optStat),null));
                return;
         }

    // try to look for a transaction
         Identifier key=msg.getTransactionId();
         printLog("DEBUG: transaction-id: "+key,LogLevel.MEDIUM);
         if (listeners.containsKey(key))
         {  printLog("message passed to transaction: "+key,LogLevel.MEDIUM);
            ((SipProviderListener)listeners.get(key)).onReceivedMessage(this,msg);
            return;
         }
         // try to look for a dialog
         key=msg.getDialogId();
         printLog("DEBUG: dialog-id: "+key,LogLevel.MEDIUM);
         if (listeners.containsKey(key))
         {  printLog("message passed to dialog: "+key,LogLevel.MEDIUM);
            ((SipProviderListener)listeners.get(key)).onReceivedMessage(this,msg);
            return;
         }
         // try to look for a UAS
         key=msg.getMethodId();
         if (msg.isRequest() && msg.isInvite())
              {
               // need to synchronize invite requests to avoid issues
               synchronized (inviteLock)
          {
         if (listeners.containsKey(key))
         {  printLog("message passed to uas: "+key,LogLevel.MEDIUM);
            ((SipProviderListener)listeners.get(key)).onReceivedMessage(this,msg);
            return;
         }
           }
              }
              else if (listeners.containsKey(key))
              {
               printLog("message passed to uas: "+key ,LogLevel.LOW);
                 ((SipProviderListener)listeners.get(key)).onReceivedMessage(this,msg);
                 return;
         }



         // try to look for a default UA
         if (listeners.containsKey(ANY))
         {  printLog("message passed to uas: "+ANY,LogLevel.MEDIUM);
            ((SipProviderListener)listeners.get(ANY)).onReceivedMessage(this,msg);
            return;
         }

         if (msg.isRequest() && msg.isInvite())
         {
           // we are busy or can't answer - Invite would have been picked up above already if we could handle it

           if (sipBusyUrl!=null)
           {
             // redirect the call elsewhere
             String targetId=msg.getToHeader().getNameAddress().toString().replaceAll("(?i).*sip:<?([^@<]+)@.*", "$1");
             String redirect_Url=sipBusyUrl.replaceAll("calleeid", targetId);

                printLog("Incoming SIP Call - Channel busy - Redirect to: "+redirect_Url ,LogLevel.LOW);

                Message resp=MessageFactory.createResponse(msg,302,SipResponses.reasonOf(302),new NameAddress(redirect_Url));
               InviteTransactionServer ts=new InviteTransactionServer(this,msg,null);
                 ts.respondWith(resp);
              return;
           }

            printLog("Invite Request - Sending busy response (486)",LogLevel.MEDIUM);
            InviteTransactionServer ts=new InviteTransactionServer(this,msg,null);
              ts.respondWith(MessageFactory.createResponse(msg,486,SipResponses.reasonOf(486),null));
           return;
         }

         if (msg.isRequest() && msg.isNotify())
         {
           // nobody handled it, just respond 405 (method not allowed) to get it to go away
            printLog("Notify Request - Sending unsupported response (405)",LogLevel.MEDIUM);
            TransactionServer ts=new TransactionServer(this,msg,null);
              ts.respondWith(MessageFactory.createResponse(msg,405,SipResponses.reasonOf(405),null));
           return;
         }

        // if we are here, no listener_ID matched..
         printLog("No SipListener found matching that message: message DISCARDED",LogLevel.HIGH);
         //printLog("Pending SipProviderListeners= "+getListeners().size(),3);
         printLog("Pending SipProviderListeners= "+listeners.size(),LogLevel.MEDIUM);
      }
      catch (Exception e)
      {  printWarning("Error handling a new incoming message",LogLevel.HIGH);
         printException(e,LogLevel.MEDIUM);
         if (exception_listeners==null || exception_listeners.size()==0)
         {  System.err.println("Error handling a new incoming message");
            e.printStackTrace();
         }
         else
         for (Iterator i=exception_listeners.iterator(); i.hasNext(); )
               try
               {  ((SipProviderExceptionListener)i.next()).onMessageException(msg,e);
               }
               catch (Exception e2)
               {  printWarning("Error handling handling the Exception",LogLevel.HIGH);
                  printException(e2,LogLevel.MEDIUM);
               }
         }
      }
   }
  public void setOPTIONHandler(OptionHandler oh)
     {
       this.optionHandler=oh;
   }

   /** Adds a new Connection */
   private void addConnection(ConnectedTransport conn)
   {  ConnectionIdentifier conn_id=new ConnectionIdentifier(conn);
      if (connections.containsKey(conn_id))
      {  // remove the previous connection
         printLog("trying to add the already established connection "+conn_id,LogLevel.HIGH);
         printLog("connection "+conn_id+" will be replaced",LogLevel.HIGH);
         ConnectedTransport old_conn=(ConnectedTransport)connections.get(conn_id);
         old_conn.halt();
         connections.remove(conn_id);
      }
      else
      if (connections.size()>=nmax_connections)
      {  // remove the older unused connection
         printLog("reached the maximum number of connection: removing the older unused connection",LogLevel.HIGH);
         long older_time=System.currentTimeMillis();
         ConnectionIdentifier older_id=null;
         for (Enumeration e=connections.elements(); e.hasMoreElements(); )
         {  ConnectedTransport co=(ConnectedTransport)e.nextElement();
            if (co.getLastTimeMillis()<older_time) older_id=new ConnectionIdentifier(co);
         }
         if (older_id!=null) removeConnection(older_id);
      }
      connections.put(conn_id,conn);
      conn_id=new ConnectionIdentifier(conn);
      conn=(ConnectedTransport)connections.get(conn_id);
      // DEBUG log:
      printLog("active connenctions:",LogLevel.LOW);
      for (Enumeration e=connections.keys(); e.hasMoreElements(); )
      {  ConnectionIdentifier id=(ConnectionIdentifier)e.nextElement();
         printLog("conn-id="+id+": "+((ConnectedTransport)connections.get(id)).toString(),LogLevel.LOW);
      }
   }


   /** Removes a Connection */
   private void removeConnection(ConnectionIdentifier conn_id)
   {  if (connections.containsKey(conn_id))
      {  ConnectedTransport conn=(ConnectedTransport)connections.get(conn_id);
         conn.halt();
         connections.remove(conn_id);
         // DEBUG log:
         printLog("active connenctions:",LogLevel.LOW);
         for (Enumeration e=connections.elements(); e.hasMoreElements(); )
         {  ConnectedTransport co=(ConnectedTransport)e.nextElement();
            printLog("conn "+co.toString(),LogLevel.LOW);
         }
      }
   }


   //************************* Callback methods *************************

   /** When a new SIP message is received. */
   public void onReceivedMessage(Transport transport, Message msg)
   {  processReceivedMessage(msg);
   }


   /** When Transport terminates. */
   public void onTransportTerminated(Transport transport, Exception error)
   {  printLog("transport "+transport+" terminated",LogLevel.MEDIUM);
      if (transport.getProtocol().equals(PROTO_TCP))
      {  ConnectionIdentifier conn_id=new ConnectionIdentifier((ConnectedTransport)transport);
         removeConnection(conn_id);
      }
      if (error!=null)
         printException(error,LogLevel.HIGH);
   }


   /** When a new incoming Connection is established */
   public void onIncomingConnection(TcpServer tcp_server, TcpSocket socket)
   {  printLog("incoming connection from "+socket.getAddress()+":"+socket.getPort(),LogLevel.MEDIUM);
      ConnectedTransport conn=new TcpTransport(socket,this);
      printLog("tcp connection "+conn+" opened",LogLevel.MEDIUM);
      addConnection(conn);
   }


   /** When TcpServer terminates. */
   public void onServerTerminated(TcpServer tcp_server, Exception error)
   {  printLog("tcp server "+tcp_server+" terminated",LogLevel.MEDIUM);
   }



   //************************** Other methods ***************************

   /** Picks a fresh branch value.
     * The branch ID MUST be unique across space and time for
     * all requests sent by the UA.
     * The branch ID always begin with the characters "z9hG4bK". These
     * 7 characters are used by RFC 3261 as a magic cookie. */
   public static String pickBranch()
   {  //String str=Long.toString(Math.abs(Random.nextLong()),16);
      //if (str.length()<5) str+="00000";
      //return "z9hG4bK"+str.substring(0,5);
      return "z9hG4bK"+Random.nextNumString(5)+((breaker++)%100000);
   }

   /** Picks an unique branch value based on a SIP message.
     * This value could also be used as transaction ID */
   public String pickBranch(Message msg)
   {  StringBuffer sb=new StringBuffer();
      sb.append(msg.getRequestLine().getAddress().toString());
      sb.append(getViaAddress()+getPort());
      ViaHeader top_via=msg.getViaHeader();
      if (top_via.hasBranch())
         sb.append(top_via.getBranch());
      else
      sb.append(top_via.getHost()+top_via.getPort());
         sb.append(msg.getCSeqHeader().getSequenceNumber());
         sb.append(msg.getCallIdHeader().getCallId());
         sb.append(msg.getFromHeader().getTag());
         sb.append(msg.getToHeader().getTag());
      }
      //return "z9hG4bK"+(new MD5(unique_str)).asHex().substring(0,9);
      return "z9hG4bK"+(new SimpleDigest(5,sb.toString())).asHex();
   }


   /** Picks a new tag.
     * A tag  MUST be globally unique and cryptographically random
     * with at least 32 bits of randomness.  A property of this selection
     * requirement is that a UA will place a different tag into the From
     * header of an INVITE than it would place into the To header of the
     * response to the same INVITE.  This is needed in order for a UA to
     * invite itself to a session. */
   public static String pickTag()
   {  //String str=Long.toString(Math.abs(Random.nextLong()),16);
      //if (str.length()<8) str+="00000000";
      //return str.substring(0,8);
      return "z9hG4bK"+Random.nextNumString(8);
   }

   /** Picks a new tag. The tag is generated uniquely based on message <i>req</i>.
     * This tag can be generated for responses in a stateless
     * manner - in a manner that will generate the same tag for the
     * same request consistently.
     */
   public static String pickTag(Message req)
   {  //return String.valueOf(tag_generator++);
      //return (new MD5(request.toString())).asHex().substring(0,8);
      return (new SimpleDigest(8,req.toString())).asHex();
   }


   /** Picks a new call-id.
     * The call-id is a globally unique
     * identifier over space and time. It is implemented in the
     * form "localid@host". Call-id must be considered case-sensitive and is
     * compared byte-by-byte. */
   public String pickCallId()
   {  //String str=Long.toString(Math.abs(Random.nextLong()),16);
      //if (str.length()<12) str+="000000000000";
      //return str.substring(0,12)+"@"+getViaAddress();
      return Random.nextNumString(12)+"@"+getViaAddress();
   }


   /** picks an initial CSeq */
   public static int pickInitialCSeq()
   {  return 1;
   }


   /** (<b>Deprecated</b>) Constructs a NameAddress based on an input string.
     * The input string can be a:
     * <br> - <i>user</i> name,
     * <br> - <i>user@address</i> url,
     * <br> - <i>"Name" &lt;sip:user@address&gt;</i> address,
     * <p>
     * In the former case,
     * a SIP URL is costructed using the outbound proxy as host address if present,
     * otherwise the local via address is used. */
   public NameAddress completeNameAddress(String str)
   { 
     if (str.indexOf("<sip:") >= 0) return new NameAddress(str);
     else
       SipURL url = completeSipURL(str);
       return new NameAddress(url);
      }
   }
   /** Constructs a SipURL based on an input string. */
   private SipURL completeSipURL(String str)
   { 
     // in case it is passed only the 'user' field, add '@'<outbound_proxy>[':'<outbound_port>]
     if (!str.startsWith("sip:") && str.indexOf("@")<0 && str.indexOf(".") < 0 && str.indexOf(":") < 0) { 
       // may be it is just the user name..
       String url = "sip:" + str + "@";
       if (outbound_proxy != null) { 
         url += outbound_proxy.getAddress().toString();
         int port = outbound_proxy.getPort();
         if (port > 0 && port != SipStack.default_port) url += ":" + port;
         } else
           url += via_addr;
           if (host_port > 0 && host_port != SipStack.default_port) url += ":" + host_port;
         }
         return new SipURL(url);
      }
      else return new SipURL(str);
   }

   /** Constructs a SipURL for the given <i>username</i> on the local SIP UA.
     * If <i>username</i> is null, only host address and port are used. */
   /*public SipURL getSipURL(String user_name)
   {  return new SipURL(user_name,via_addr,(host_port!=SipStack.default_port)?host_port:-1);
   }*/


   //******************************* Logs *******************************

   /** Gets a String value for this object */
   public String toString()
   {  if (host_ipaddr==null) return host_port+"/"+transportProtocolsToString();
      else return host_ipaddr.toString()+":"+host_port+"/"+transportProtocolsToString();
   }

   /** Adds a new string to the default Log */
   private final void printLog(String str, int level)
   {  if (event_log!=null)
      {  String provider_id=(host_ipaddr==null)? Integer.toString(host_port) : host_ipaddr.toString()+":"+host_port;
         event_log.println("SipProvider-"+provider_id+": "+str,level+SipStack.LOG_LEVEL_TRANSPORT);
      }
   }

   /** Adds a WARNING to the default Log */
   private final void printWarning(String str, int level)
   {  printLog("WARNING: "+str,level);
   }

   /** Adds the Exception message to the default Log */
   private final void printException(Exception e, int level)
   {  if (event_log!=null) event_log.printException(e,level+SipStack.LOG_LEVEL_TRANSPORT);
   }

   /** Adds the SIP message to the messageslog */
   private final void printMessageLog(String proto, String addr, int port, int len, Message msg, String str)
   {  if (log_all_packets || len>=MIN_MESSAGE_LENGTH)
      {  if (message_log!=null)
         {  message_log.printPacketTimestamp(proto,addr,port,len,str+"\r\n"+msg.toString()+"-----End-of-message-----\r\n",1);
         }
         if (event_log!=null)
         {  String first_line=msg.getFirstLine();
            if (first_line!=null) first_line=first_line.trim(); else first_line="NOT a SIP message";
            event_log.print("\r\n");
            event_log.printPacketTimestamp(proto,addr,port,len,first_line+", "+str,1);
            event_log.print("\r\n");
         }
      }
   }

}
TOP

Related Classes of org.zoolu.sip.provider.SipProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.