/** Close the message channel. */
  public void close() {
    try {
      if (LogWriter.needsLogging) LogWriter.logMessage("Closing message Channel " + this);

      if (mySock != null) {
        try {
          String addr = mySock.getAddress();
          int port = mySock.getPort();
          stack.ioHandler.removeAndCloseSocket(stack.ioHandler.makeKey(addr, port));
        } catch (IOException e) {
          if (LogWriter.needsLogging) LogWriter.logMessage("Socket was already closed for " + this);
        }
        mySock = null;
      } else {
        if (LogWriter.needsLogging) LogWriter.logMessage("Socket was already null for " + this);
      }

    } catch (Exception ex) {
      if (LogWriter.needsLogging) {
        LogWriter.logMessage("Exception closing message Channel " + this);
        LogWriter.logException(ex);
      }
    } finally {
      this.isRunning = false;
      uncache();
    }
  }
 /**
  * Exception processor for exceptions detected from the application.
  *
  * @param ex The exception that was generated.
  */
 public void handleException(SIPServerException ex) {
   // Return a parse error message to the client on the other end
   // if he is still alive.
   int rc = ex.getRC();
   String msgString = ex.getMessage();
   if (rc != 0) {
     // Do we have a valid Return code ? --
     // in this case format the message.
     Request request = (Request) ex.getSIPMessage();
     Response response = request.createResponse(rc);
     try {
       sendMessage(response);
     } catch (IOException ioex) {
       if (LogWriter.needsLogging) LogWriter.logException(ioex);
     }
   } else {
     // Otherwise, message is already formatted --
     // just return it
     try {
       sendMessage(msgString.getBytes(), false);
     } catch (IOException ioex) {
       if (LogWriter.needsLogging) LogWriter.logException(ioex);
     }
   }
 }
  /** Run method specified by runnable. */
  public void run() {

    while (true) {
      // Create a new string message parser to parse the list of messages.
      if (myParser == null) {
        myParser = new StringMsgParser();
        myParser.setParseExceptionListener(this);
      }

      // messages that we write out to the peer.
      Datagram packet;

      if (stack.threadPoolSize != -1) {
        synchronized (((UDPMessageProcessor) messageProcessor).messageQueue) {
          while (((UDPMessageProcessor) messageProcessor).messageQueue.isEmpty()) {

            // Check to see if we need to exit.
            if (!((UDPMessageProcessor) messageProcessor).running) return;

            try {
              // Wait for packets
              ((UDPMessageProcessor) messageProcessor).messageQueue.wait();
            } catch (InterruptedException ex) {
              if (!((UDPMessageProcessor) messageProcessor).running) return;
            }
          }
          packet = (Datagram) ((UDPMessageProcessor) messageProcessor).messageQueue.firstElement();
          ((UDPMessageProcessor) messageProcessor).messageQueue.removeElementAt(0);
        }
        incomingPacket = packet;
      } else {
        packet = incomingPacket;
      }

      if (LogWriter.needsLogging) {
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG, "Processing new incoming datagram " + packet.getLength());
      }
      // Process the packet. Catch and log any exception we may throw.
      try {
        processIncomingDataPacket(packet);
      } catch (Throwable e) {
        if (LogWriter.needsLogging) {
          LogWriter.logMessage(
              LogWriter.TRACE_EXCEPTION, "Exception processing incoming UDP packet");
          LogWriter.logException((Exception) e);
        }
      }

      if (stack.threadPoolSize == -1) {
        return;
      }
    }
  }
  /**
   * This gets invoked when thread.start is called from the constructor. Implements a message loop -
   * reading the tcp connection and processing messages until we are done or the other end has
   * closed.
   */
  public void run() {
    if (LogWriter.needsLogging)
      LogWriter.logMessage(
          LogWriter.TRACE_DEBUG,
          "New TCP Message Channel Thread started " + this + " " + Thread.currentThread());
    // Removed by ArnauVP: in MIDP it's not reliable to read in blocks
    // from inputstreams; thus the Pipe was useless for byte-to-byte reads,
    // and was giving sync problems when reading from the pipe (duplicate bytes).
    // Create a pipeline to connect to our message parser.
    //        Pipeline hispipe = null;
    //        hispipe = new Pipeline(myClientInputStream, stack.readTimeout,
    //                ((SIPTransactionStack) stack).getTimer());

    // Create a pipelined message parser to read from inputStream
    // and parse SIP messages from the bytes.
    myParser =
        new PipelinedMsgParser(
            this, myClientInputStream, this.messageProcessor.getMaximumMessageSize());
    myParser.setMessageChannel(this);
    // Start running the parser thread.
    myParser.processInput();
    // Changed by ArnauVP: using a buffer in some implementations of MIDP,
    // the call to read(msg) would not return from until BUFFER_SIZE bytes were available,
    // thus delaying the processing of the responses by the application and causing timeout.
    // bug fix by Emmanuel Proulx
    //        int bufferSize = 4096;
    this.tcpMessageProcessor.useCount++;
    this.isRunning = true;
  }
  /**
   * Constructor - gets called from the SIPMessageStack class with a socket on accepting a new
   * client. All the processing of the message is done here with the stack being freed up to handle
   * new connections. The sock input is the socket that is returned from the accept. Global data
   * that is shared by all threads is accessible in the Server structure.
   *
   * @param sock Socket from which to read and write messages. The socket is already connected (was
   *     created as a result of an accept).
   * @param sipStack the SIP Stack
   * @param channelNotifier Notifier (optional) that gets called when the channel is opened or
   *     closed.
   */
  protected TCPMessageChannel(
      SocketConnection sock, SIPMessageStack sipStack, TCPMessageProcessor msgProcessor)
      throws IOException {

    stack = sipStack;
    mySock = sock;
    myAddress = sipStack.getHostAddress();
    peerAddress = sock.getAddress();
    myClientInputStream = sock.openInputStream();
    myClientOutputStream = sock.openOutputStream();
    if (LogWriter.needsLogging) {
      LogWriter.logMessage("Creating new TCPMessageChannel " + this);
      LogWriter.logMessage(
          LogWriter.TRACE_DEBUG,
          "Channel parameters: "
              + "stack: "
              + stack
              + "\n"
              + "processor: "
              + msgProcessor
              + "\n"
              + "localAddress: "
              + myAddress
              + "\n"
              + "peerAddress: "
              + peerAddress
              + "\n"
              + "IS "
              + myClientInputStream
              + " Socket "
              + mySock);
    }
    stack.ioHandler.putSocket(
        stack.ioHandler.makeKey(mySock.getAddress(), mySock.getPort()),
        mySock,
        myClientOutputStream,
        myClientInputStream);

    mythread = new Thread(this, "TCPMessageChannel - incoming connection");

    this.tcpMessageProcessor = msgProcessor;
    this.myPort = this.tcpMessageProcessor.getPort();
    // Bug report by Vishwashanti Raj Kadiayl
    super.messageProcessor = msgProcessor;
    mythread.start();
  }
  public void closeFromParser() {
    try {
      if (LogWriter.needsLogging)
        LogWriter.logMessage("Closing message Channel " + this + " from Parser");

      if (mySock != null) {
        try {
          String addr = mySock.getAddress();
          int port = mySock.getPort();
          stack.ioHandler.removeAndCloseSocket(stack.ioHandler.makeKey(addr, port));
        } catch (IOException e) {
          if (LogWriter.needsLogging) LogWriter.logMessage("Socket was already closed for " + this);
        }
        mySock = null;
      } else {
        if (LogWriter.needsLogging) LogWriter.logMessage("Socket was already null for " + this);
      }

    } catch (Exception ex) {
      if (LogWriter.needsLogging && !stack.toExit) {
        LogWriter.logMessage(
            LogWriter.TRACE_EXCEPTION, "Exception closing message Channel " + this);
        LogWriter.logException(ex);
      }
    } finally {
      this.isRunning = false;
      uncache();
      this.tcpMessageProcessor.useCount--;
      if (LogWriter.needsLogging)
        LogWriter.logMessage("TCP Message Processor use count: " + tcpMessageProcessor.useCount);
    }
  }
  /**
   * Exception processor for exceptions detected from the parser. (This is invoked by the parser
   * when an error is detected).
   *
   * @param sipMessage -- the message that incurred the error.
   * @param ex -- parse exception detected by the parser.
   * @param header -- header that caused the error.
   * @throws ParseException Thrown if we want to reject the message.
   */
  public void handleException(
      ParseException ex, Message sipMessage, Class hdrClass, String header, String message)
      throws ParseException {
    if (LogWriter.needsLogging) LogWriter.logException(ex);
    // Log the bad message for later reference.

    if (hdrClass.equals(FromHeader.clazz)
        || hdrClass.equals(ToHeader.clazz)
        || hdrClass.equals(CSeqHeader.clazz)
        || hdrClass.equals(ViaHeader.clazz)
        || hdrClass.equals(CallIdHeader.clazz)
        || hdrClass.equals(RequestLine.clazz)
        || hdrClass.equals(StatusLine.clazz)) {
      stack.logBadMessage(message);
      throw ex;
    } else {
      sipMessage.addUnparsed(header);
    }
  }
  /**
   * Constructor. We create one of these when we send out a message.
   *
   * @param targetAddr internet address of the place where we want to send messages.
   * @param port target port (where we want to send the message).
   * @param stack our SIP Stack.
   */
  public UDPMessageChannel(
      String targetAddr, int port, SIPMessageStack sipStack, UDPMessageProcessor processor) {
    if (LogWriter.needsLogging)
      LogWriter.logMessage(
          LogWriter.TRACE_DEBUG,
          "DEBUG, UDPMessageChannel, UDPMessageChannel(),"
              + " Creating message channel on "
              + targetAddr
              + "/"
              + port);

    stack = sipStack;
    this.peerPort = port;
    this.peerAddress = targetAddr;
    this.messageProcessor = processor;
    this.myPort = processor.getPort();
    this.myAddress = sipStack.getHostAddress();
    this.peerProtocol = "UDP";
  }
  /**
   * Send a message to a specified receiver address.
   *
   * @param msg message string to send.
   * @param receiverAddress Address of the place to send it to.
   * @param receiverPort the port to send it to.
   * @throws IOException If there is trouble sending this message.
   */
  protected void sendMessage(
      byte[] msg, String receiverAddress, int receiverPort, boolean reconnect) throws IOException {
    // msg += "\r\n\r\n";
    // Via is not included in the request so silently drop the reply.
    if (receiverPort == -1) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            "DEBUG, UDPMessageChannel, sendMessage(),"
                + " The message is not sent: the receiverPort=-1");
      throw new IOException("Receiver port not set ");
    }

    try {
      DatagramConnection socket;
      boolean created = false;

      if (stack.udpFlag) {
        // Use the socket from the message processor (for firewall
        // support use the same socket as the message processor
        // socket -- feature request # 18 from java.net). This also
        // makes the whole thing run faster!
        socket = ((UDPMessageProcessor) messageProcessor).dc;

      } else {
        // bind to any interface and port.
        // format: datagram://address:port
        String url = "datagram://" + peerAddress + ":" + peerPort;
        socket = (DatagramConnection) Connector.open(url);
        created = true;
      }

      Datagram reply = socket.newDatagram(msg, msg.length);
      socket.send(reply);
      if (created) socket.close();
    } catch (IOException ex) {
      throw ex;
    } catch (Exception ex) {
      InternalErrorHandler.handleException(ex);
    }
  }
  /**
   * Constructor - connects to the given inet address. Acknowledgement -- Lamine Brahimi (IBM
   * Zurich) sent in a bug fix for this method. A thread was being unnecessarily created.
   *
   * @param inetAddr inet address to connect to.
   * @param sipStack is the sip stack from which we are created.
   * @param messageProcessor to whom a parsed message is passed
   * @throws IOException if we cannot connect.
   */
  protected TCPMessageChannel(
      String inetAddr, int port, SIPMessageStack sipStack, TCPMessageProcessor messageProcessor)
      throws IOException {

    this.peerAddress = inetAddr;
    this.peerPort = port;
    this.myPort = messageProcessor.getPort();
    this.peerProtocol = "TCP";
    this.stack = sipStack;
    this.tcpMessageProcessor = messageProcessor;
    this.myAddress = sipStack.getHostAddress();
    // Bug report by Vishwashanti Raj Kadiayl
    super.messageProcessor = messageProcessor;
    this.key = "TCP" + ":" + stack.ioHandler.makeKey(peerAddress, peerPort);

    if (LogWriter.needsLogging)
      LogWriter.logMessage(
          "Created new TCP Message Channel "
              + this
              + " with key "
              + key
              + "\nprocessor: "
              + messageProcessor);
  }
  /**
   * Gets invoked by the parser as a callback on successful message parsing (i.e. no parser errors).
   *
   * @param sipMessage Mesage to process (this calls the application for processing the message).
   */
  public void processMessage(Message sipMessage) {

    if (!stack.isAlive()) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG,
            "MsgChannel " + this + " is dropping message as the stack is closing");
      return; // drop messages when closing, avoid Exceptions
    }

    try {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            "[TCPMessageChannel]-> Processing incoming message: " + sipMessage.getFirstLine());
      if (sipMessage.getFromHeader() == null
          || sipMessage.getTo() == null
          || sipMessage.getCallId() == null
          || sipMessage.getCSeqHeader() == null
          || sipMessage.getViaHeaders() == null) {
        String badmsg = sipMessage.encode();
        if (LogWriter.needsLogging) {
          ServerLog.logMessage("bad message " + badmsg);
          ServerLog.logMessage(">>> Dropped Bad Msg");
        }
        stack.logBadMessage(badmsg);
        return;
      }

      ViaList viaList = sipMessage.getViaHeaders();
      // For a request
      // first via header tells where the message is coming from.
      // For response, this has already been recorded in the outgoing
      // message.
      if (sipMessage instanceof Request) {
        ViaHeader v = (ViaHeader) viaList.first();
        if (v.hasPort()) {
          viaPort = v.getPort();
        } else {
          viaPort = SIPMessageStack.DEFAULT_PORT;
        }
        this.peerProtocol = v.getTransport();
        try {
          if (peerPort == -1) peerPort = mySock.getPort();
          this.peerAddress = mySock.getAddress();

          // Log this because it happens when the remote host identifies
          // as a FQDN but this is not resolvable to an IP by the OS.
          // S40 doesn't have DNS settings, for instance, so if the APN
          // is not able to resolve all the addresses of the SIP/IMS core,
          // this problem will appear.
          if (peerAddress == null && LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_EXCEPTION, "WARNING! Socket.getAddress() returned 'null'!!!");

          // Be warned, the host comparison may fail if socket.getAddress()
          // returns a domain name as the Via Host will be a numeric IP.
          // FIXME: No idea. Doing a DNS lookup or reverse DNS lookup
          // can be misleading because they can be non-matching, that is,
          // DNS(peerAddressName) != ReverseDNS(peerAddressIP)
          if (v.hasParameter(ViaHeader.RPORT) || !v.getHost().equals(this.peerAddress)) {
            if (LogWriter.needsLogging)
              LogWriter.logMessage(
                  LogWriter.TRACE_MESSAGES,
                  "WARNING! \"Received\" parameter "
                      + "has been temporarily disabled. Response will be sent to topmost Via Host: "
                      + v.getHost());
            this.peerAddress = v.getHost();
            //		                if (LogWriter.needsLogging)
            //			                   LogWriter.logMessage(LogWriter.TRACE_MESSAGES, "Adding
            // \"received\" parameter" +
            //			                   		" to incoming request with value: " + peerAddress +
            //			                   		" because it doesn't match the Via host " + v.getHost());
            //						v.setParameter(ViaHeader.RECEIVED, this.peerAddress);

          }

          if (v.hasParameter(ViaHeader.RPORT))
            v.setParameter(ViaHeader.RPORT, Integer.toString(this.peerPort));

          /*
           * If socket is invalid, close it because it is useless and dangerous.
           * Also if we ran out of slots for new sockets, as this could prevent
           * incoming connections from being accepted.
           */
          if (mySock.getAddress() == null
              || (stack.maxConnections != -1
                  && tcpMessageProcessor.getNumConnections() >= stack.maxConnections)) {
            stack.ioHandler.disposeSocket(mySock, myClientInputStream, myClientOutputStream);
            mySock = null;
            myClientInputStream = null;
            myClientOutputStream = null;
            if (stack.maxConnections != -1) {
              synchronized (tcpMessageProcessor) {
                tcpMessageProcessor.decreaseNumConnections();
                tcpMessageProcessor.notify();
              }
            }
          }
          // reuse socket even for outgoing requests
          else if (!this.isCached) {
            ((TCPMessageProcessor) this.messageProcessor).cacheMessageChannel(this);
            String key = "TCP" + ":" + stack.ioHandler.makeKey(peerAddress, peerPort);
            stack.ioHandler.putSocket(key, mySock, myClientOutputStream, myClientInputStream);
          }

        } catch (IOException e) {
          e.printStackTrace();
        }
      }

      // System.out.println("receiver address = " + receiverAddress);

      // For each part of the request header, fetch it and process it
      long receptionTime = System.currentTimeMillis();

      if (sipMessage instanceof Request) {
        // This is a request - process the request.
        Request sipRequest = (Request) sipMessage;
        // Create a new sever side request processor for this
        // message and let it handle the rest.

        if (LogWriter.needsLogging) {
          LogWriter.logMessage("----Processing Message---");
        }

        // TODO: check maximum size of request

        SIPServerRequestInterface sipServerRequest = stack.newSIPServerRequest(sipRequest, this);

        if (sipServerRequest != null) {
          try {
            sipServerRequest.processRequest(sipRequest, this);

            ServerLog.logMessage(
                sipMessage,
                sipRequest.getViaHost() + ":" + sipRequest.getViaPort(),
                stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
                false,
                receptionTime);
          } catch (SIPServerException ex) {
            ServerLog.logMessage(
                sipMessage,
                sipRequest.getViaHost() + ":" + sipRequest.getViaPort(),
                stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
                ex.getMessage(),
                false,
                receptionTime);
            handleException(ex);
          }

        } else {
          if (LogWriter.needsLogging)
            LogWriter.logMessage("Dropping request -- null sipServerRequest");
        }

      } else {
        // This is a response message - process it.
        Response sipResponse = (Response) sipMessage;

        // TODO: check maximum size of the response

        SIPServerResponseInterface sipServerResponse =
            stack.newSIPServerResponse(sipResponse, this);

        if (LogWriter.needsLogging)
          LogWriter.logMessage("got a response interface " + sipServerResponse);

        try {
          // Responses with no ClienTransaction associated will not be processed
          // as they may cause a NPE in the EventScanner thread.
          if (sipServerResponse != null) sipServerResponse.processResponse(sipResponse, this);
          else {
            if (LogWriter.needsLogging) {
              LogWriter.logMessage("null sipServerResponse!");
            }
          }
        } catch (SIPServerException ex) {
          // An error occured processing the message -- just log it.
          ServerLog.logMessage(
              sipMessage,
              getPeerAddress().toString() + ":" + getPeerPort(),
              stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
              ex.getMessage(),
              false,
              receptionTime);
          // Ignore errors while processing responses??
        }
      }
    } catch (Exception ee) {
      if (stack.isAlive()) {
        throw new RuntimeException(ee.getClass() + ":" + ee.getMessage());
      }
      // else ignore exceptions
    } finally {
      //            this.tcpMessageProcessor.useCount --;
    }
  }
  /**
   * Send message to whoever is connected to us. Uses the topmost via address to send to.
   *
   * @param message is the message to send.
   */
  private void sendMessage(byte[] msg, boolean retry) throws IOException {

    int portToUse;
    if (viaPort != -1) portToUse = viaPort;
    else portToUse = peerPort;

    if (!this.isRunning && !this.isCached) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_MESSAGES,
            "Tried to send message through a Message Channel that is no longer running. Create a new one.");

      TCPMessageChannel newChannel =
          (TCPMessageChannel)
              this.tcpMessageProcessor.createMessageChannel(this.peerAddress, portToUse);
      newChannel.sendMessage(msg, retry);
      return;
    }

    SocketConnection sock =
        stack.ioHandler.sendBytes(this.peerAddress, portToUse, this.peerProtocol, msg, retry);

    /*Fix: don't replace the original socket (where the incoming
    request came from) with the one created to send the response
    to the port specified by the VIA header. This would cause a
    NPE later, as the 'via socket' will be closed shortly after
    sending the response. Thanks to Janos Vig (Genaker) for
    detecting this. */
    if (sock != null
        && (mySock == null || (sock != mySock && sock.getPort() == mySock.getPort()))) {
      try {
        if (mySock != null) {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_MESSAGES,
                "Closing socket on TCPMessageChannel and replacing with new one");

          // Closing the IO streams will also stop the parser reading from them
          if (myClientOutputStream != null) myClientOutputStream.close();
          if (myClientInputStream != null) myClientInputStream.close();
          mySock.close();

        } else {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_DEBUG, "TCP Msg channel " + this + " socket was null!");
        }
      } catch (IOException ex) {
        ex.printStackTrace();
      }

      // replace the old references of socket and IO streams
      // to the ones pointing to the newly created socket.
      mySock = sock;
      myClientOutputStream = stack.ioHandler.getSocketOutputStream(mySock);
      myClientInputStream = stack.ioHandler.getSocketInputStream(mySock);

      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG,
            "Creating new Thread from Message Channel "
                + this
                + " and new socket "
                + sock
                + " IS "
                + myClientInputStream);
      Thread thread = new Thread(this);
      thread.start();
    }
  }
  /**
   * Send a message to a specified receiver address.
   *
   * @param msg message string to send.
   * @param receiverAddress Address of the place to send it to.
   * @param receiverPort the port to send it to.
   * @param receiverProtocol protocol to use to send.
   * @param retry try if connection was not successful at fi
   * @throws IOException If there is trouble sending this message.
   */
  protected void sendMessage(
      byte[] msg, String receiverAddress, int receiverPort, String receiverProtocol, boolean retry)
      throws IOException {
    if (LogWriter.needsLogging)
      LogWriter.logMessage(
          "Sending message to ["
              + receiverAddress
              + ":"
              + receiverPort
              + "/"
              + receiverProtocol
              + "]\n"
              + new String(msg)
              + "  to be sent to  ");

    // msg += "\r\n\r\n";
    // Via is not included in the request so silently drop the reply.
    if (receiverPort == -1) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            "DEBUG, UDPMessageChannel, sendMessage(),"
                + " The message is not sent: the receiverPort=-1");
      throw new IOException("Receiver port not set ");
    }
    if (Utils.compareToIgnoreCase(receiverProtocol, "UDP") == 0) {

      try {
        DatagramConnection socket;
        Datagram reply;
        boolean created = false;

        if (stack.udpFlag) {
          // Use the socket from the message processor (for firewall
          // support use the same socket as the message processor
          // socket -- feature request # 18 from java.net). This also
          // makes the whole thing run faster!
          socket = ((UDPMessageProcessor) messageProcessor).dc;

          // ArnauVP: this has the problem that the datagram created from this (inbound) connection
          // doesn't have an address assigned. Let's do it.
          reply = socket.newDatagram(msg, msg.length);
          reply.setAddress("datagram://" + peerAddress + ":" + peerPort);

        } else {
          // bind to any interface and port.
          // format: datagram://address:port
          socket = stack.getNetworkLayer().createDatagramSocket(peerAddress, peerPort);
          reply = socket.newDatagram(msg, msg.length);
          created = true;
        }
        if (LogWriter.needsLogging)
          LogWriter.logMessage(
              LogWriter.TRACE_DEBUG,
              "UDPMessageChannel, sendMessage(),"
                  + " Sending message over UDP, using socket "
                  + socket
                  + " created "
                  + created
                  + " destination "
                  + reply.getAddress());
        socket.send(reply);
        if (created) socket.close();
      } catch (IOException ex) {
        throw ex;
      } catch (Exception ex) {
        InternalErrorHandler.handleException(ex);
      }

    } else {
      // Use TCP to talk back to the sender.
      SocketConnection outputSocket =
          stack.ioHandler.sendBytes(peerAddress, peerPort, "tcp", msg, retry);
      OutputStream myOutputStream = stack.ioHandler.getSocketOutputStream(outputSocket);
      myOutputStream.write(msg, 0, msg.length);
      myOutputStream.flush();
      // The socket is cached (don't close it!);
    }
  }
  /**
   * Actually process the parsed SIP message.
   *
   * @param sipMessage
   */
  public void processMessage(Message sipMessage) {

    if (sipMessage instanceof Request) {
      Request sipRequest = (Request) sipMessage;

      // This is a request - process it.
      SIPServerRequestInterface sipServerRequest = stack.newSIPServerRequest(sipRequest, this);
      // Drop it if there is no request returned
      if (sipServerRequest == null) {
        if (LogWriter.needsLogging) {
          LogWriter.logMessage("Null request interface returned");
        }
        return;
      }
      try {
        if (LogWriter.needsLogging)
          LogWriter.logMessage(
              "About to process " + sipRequest.getFirstLine() + "/" + sipServerRequest);
        sipServerRequest.processRequest(sipRequest, this);
        if (LogWriter.needsLogging)
          LogWriter.logMessage(
              "Done processing " + sipRequest.getFirstLine() + "/" + sipServerRequest);

        // So far so good -- we will commit this message if
        // all processing is OK.
        if (ServerLog.needsLogging(ServerLog.TRACE_MESSAGES)) {
          if (sipServerRequest.getProcessingInfo() == null) {
            ServerLog.logMessage(
                sipMessage,
                sipRequest.getViaHost() + ":" + sipRequest.getViaPort(),
                stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
                false,
                new Long(receptionTime).toString());
          } else {
            ServerLog.logMessage(
                sipMessage,
                sipRequest.getViaHost() + ":" + sipRequest.getViaPort(),
                stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
                sipServerRequest.getProcessingInfo(),
                false,
                new Long(receptionTime).toString());
          }
        }
      } catch (SIPServerException ex) {

        if (ServerLog.needsLogging(ServerLog.TRACE_MESSAGES)) {
          ServerLog.logMessage(
              sipMessage,
              sipRequest.getViaHost() + ":" + sipRequest.getViaPort(),
              stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
              ex.getMessage(),
              false,
              new Long(receptionTime).toString());
        }
        handleException(ex);
      }

    } else {

      // Handle a SIP Response message.
      Response sipResponse = (Response) sipMessage;
      SIPServerResponseInterface sipServerResponse = stack.newSIPServerResponse(sipResponse, this);
      try {
        if (sipServerResponse != null) {
          sipServerResponse.processResponse(sipResponse, this);
          // Normal processing of message.
        } else {
          if (LogWriter.needsLogging) {
            LogWriter.logMessage("null sipServerResponse!");
          }
        }

      } catch (SIPServerException ex) {
        if (ServerLog.needsLogging(ServerLog.TRACE_MESSAGES)) {
          this.logResponse(sipResponse, receptionTime, ex.getMessage() + "-- Dropped!");
        }

        ServerLog.logException(ex);
      }
    }
  }
  /**
   * Process an incoming datagram
   *
   * @param packet is the incoming datagram packet.
   */
  private void processIncomingDataPacket(Datagram packet) throws Exception {

    // For a request first via header tells where the message
    // is coming from.
    // For response, just get the port from the packet.
    // format: address:port
    String address = packet.getAddress();
    try {
      int firstColon = address.indexOf("//");
      int secondColon = address.indexOf(":", firstColon + 1);
      this.peerAddress = address.substring(firstColon + 2, secondColon);
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG, "UDPMessageChannel, run(), sender address:" + peerAddress);
      String senderPortString = address.substring(address.indexOf(";") + 1, address.indexOf("|"));
      this.peerPacketSourcePort = Integer.parseInt(senderPortString);
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG, "UDPMessageChannel, run(), sender port:" + peerPacketSourcePort);
    } catch (NumberFormatException e) {

      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_EXCEPTION,
            "UDPMessageChannel, run(), exception raised: " + e.getMessage());
      e.printStackTrace();
      peerPacketSourcePort = -1;
    }

    int packetLength = packet.getLength();
    // Read bytes and put it in a queue.
    byte[] msgBytes = packet.getData();

    // Do debug logging.
    if (LogWriter.needsLogging) {
      LogWriter.logMessage(
          LogWriter.TRACE_DEBUG,
          "UDPMessageChannel: processIncomingDataPacket : peerAddress = "
              + peerAddress
              + "/"
              + peerPacketSourcePort
              + " Length = "
              + packetLength
              + " msgBytes "
              + msgBytes);
    }

    Message sipMessage = null;
    try {
      receptionTime = System.currentTimeMillis();
      sipMessage = myParser.parseSIPMessage(msgBytes);
      myParser = null;
    } catch (ParseException ex) {
      myParser = null; // let go of the parser reference.
      if (LogWriter.needsLogging) {
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "Rejecting message !  " + new String(msgBytes));
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "error message " + ex.getMessage());
        LogWriter.logException(ex);
      }

      // TODO: do this on TCP too
      // JvB: send a 400 response for requests (except ACK)
      String msgString = new String(msgBytes, 0, packetLength);
      if (!msgString.startsWith("SIP/") && !msgString.startsWith("ACK ")) {

        String badReqRes = create400Response(msgString, ex);
        if (badReqRes != null) {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_DEBUG, "Sending automatic 400 Bad Request: " + badReqRes);
          try {
            this.sendMessage(badReqRes.getBytes(), peerAddress, peerPacketSourcePort, "UDP", false);
          } catch (IOException e) {
            LogWriter.logException(e);
          }
        } else {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_DEBUG, "Could not formulate automatic 400 Bad Request");
        }
      }

      return;
    }
    // No parse exception but null message - reject it and
    // march on (or return).
    // exit this message processor if the message did not parse.
    if (sipMessage == null) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "Rejecting message !  + Null message parsed.");
      return;
    }

    ViaList viaList = sipMessage.getViaHeaders();

    // Check for the required headers.
    if (sipMessage.getFromHeader() == null
        ||
        // sipMessage.getFromHeader().getTag() == null  ||
        sipMessage.getTo() == null
        || sipMessage.getCallId() == null
        || sipMessage.getCSeqHeader() == null
        || sipMessage.getViaHeaders() == null) {
      String badmsg = new String(msgBytes);
      if (LogWriter.needsLogging) {
        LogWriter.logMessage("bad message " + badmsg);
        LogWriter.logMessage(
            ">>> Dropped Bad Msg "
                + "FromHeader = "
                + sipMessage.getFromHeader()
                + "ToHeader = "
                + sipMessage.getTo()
                + "CallId = "
                + sipMessage.getCallId()
                + "CSeqHeader = "
                + sipMessage.getCSeqHeader()
                + "Via = "
                + sipMessage.getViaHeaders());
      }

      stack.logBadMessage(badmsg);
      return;
    }

    // For a request first via header tells where the message
    // is coming from.
    // For response, just get the port from the packet.
    if (sipMessage instanceof Request) {

      ViaHeader v = (ViaHeader) viaList.first();
      if (v.hasPort()) this.peerPort = v.getPort();
      else this.peerPort = SIPMessageStack.DEFAULT_PORT;

      this.peerProtocol = v.getTransport();

      boolean hasRPort = v.hasParameter(ViaHeader.RPORT);
      // Be warned, the host comparison may fail if socket.getAddress()
      // returns a domain name as the Via Host will be a numeric IP.
      // FIXME: No idea. Doing a DNS lookup or reverse DNS lookup
      // can be misleading because they can be non-matching, that is,
      // DNS(peerAddressName) != ReverseDNS(peerAddressIP)
      if (hasRPort || !this.peerAddress.equals(v.getHost())) {
        if (LogWriter.needsLogging)
          LogWriter.logMessage(
              LogWriter.TRACE_MESSAGES,
              "WARNING! \"Received\" parameter "
                  + "has been temporarily disabled. Response will be sent to topmost Via Host: "
                  + v.getHost());
        this.peerAddress = v.getHost();
        //                if (LogWriter.needsLogging)
        //	                   LogWriter.logMessage(LogWriter.TRACE_MESSAGES, "Adding \"received\"
        // parameter" +
        //	                   		" to incoming request with value: " + peerAddress +
        //	                   		" because it doesn't match the Via host " + v.getHost());
        //				v.setParameter(ViaHeader.RECEIVED, this.peerAddress);
      }

      if (hasRPort) {
        v.setParameter(ViaHeader.RPORT, Integer.toString(peerPacketSourcePort));
        this.peerPort = peerPacketSourcePort;
      }

    } else {
      this.peerPort = this.peerPacketSourcePort;
      this.peerProtocol = ((ViaHeader) viaList.getFirst()).getTransport();
    }

    processMessage(sipMessage);
  }