/**
   * This is input reading thread for the pipelined parser. You feed it input through the input
   * stream (see the constructor) and it calls back an event listener interface for message
   * processing or error. It cleans up the input - dealing with things like line continuation
   */
  public void run() {

    Pipeline inputStream = this.rawInputStream;
    // inputStream = new MyFilterInputStream(this.rawInputStream);
    // I cannot use buffered reader here because we may need to switch
    // encodings to read the message body.
    try {
      while (true) {
        this.sizeCounter = this.maxMessageSize;
        // this.messageSize = 0;
        StringBuffer inputBuffer = new StringBuffer();

        if (Debug.parserDebug) Debug.println("Starting parse!");

        String line1;
        String line2 = null;

        while (true) {
          try {
            line1 = readLine(inputStream);
            // ignore blank lines.
            if (line1.equals("\n")) {
              if (Debug.parserDebug) Debug.println("Discarding " + line1);
              continue;
            } else break;
          } catch (IOException ex) {
            Debug.printStackTrace(ex);
            this.rawInputStream.stopTimer();
            return;
          }
        }

        inputBuffer.append(line1);
        // Guard against bad guys.
        this.rawInputStream.startTimer();

        while (true) {
          try {
            line2 = readLine(inputStream);
            inputBuffer.append(line2);
            if (line2.trim().equals("")) break;
          } catch (IOException ex) {
            this.rawInputStream.stopTimer();
            Debug.printStackTrace(ex);
            return;
          }
        }

        // Stop the timer that will kill the read.
        this.rawInputStream.stopTimer();
        inputBuffer.append(line2);
        StringMsgParser smp = new StringMsgParser(sipMessageListener);
        smp.readBody = false;
        SIPMessage sipMessage = null;

        try {
          sipMessage = smp.parseSIPMessage(inputBuffer.toString());
          if (sipMessage == null) {
            this.rawInputStream.stopTimer();
            continue;
          }
        } catch (ParseException ex) {
          // Just ignore the parse exception.
          continue;
        }

        if (Debug.parserDebug) Debug.println("Completed parsing message");
        ContentLength cl = (ContentLength) sipMessage.getContentLength();
        int contentLength = 0;
        if (cl != null) {
          contentLength = cl.getContentLength();
        } else {
          contentLength = 0;
        }

        if (Debug.parserDebug) {
          Debug.println("contentLength " + contentLength);
          Debug.println("sizeCounter " + this.sizeCounter);
          Debug.println("maxMessageSize " + this.maxMessageSize);
        }

        if (contentLength == 0) {
          sipMessage.removeContent();
        } else if (maxMessageSize == 0 || contentLength < this.sizeCounter) {
          byte[] message_body = new byte[contentLength];
          int nread = 0;
          while (nread < contentLength) {
            // Start my starvation timer.
            // This ensures that the other end
            // writes at least some data in
            // or we will close the pipe from
            // him. This prevents DOS attack
            // that takes up all our connections.
            this.rawInputStream.startTimer();
            try {

              int readlength = inputStream.read(message_body, nread, contentLength - nread);
              if (readlength > 0) {
                nread += readlength;
              } else {
                break;
              }
            } catch (IOException ex) {
              ex.printStackTrace();
              break;
            } finally {
              // Stop my starvation timer.
              this.rawInputStream.stopTimer();
            }
          }
          sipMessage.setMessageContent(message_body);
        }
        // Content length too large - process the message and
        // return error from there.
        if (sipMessageListener != null) {
          try {
            sipMessageListener.processMessage(sipMessage);
          } catch (Exception ex) {
            // fatal error in processing - close the
            // connection.
            break;
          }
        }
      }
    } finally {
      try {
        inputStream.close();
      } catch (IOException e) {
        InternalErrorHandler.handleException(e);
      }
    }
  }
  /**
   * Parse a buffer containing a single SIP Message where the body is an array of un-interpreted
   * bytes. This is intended for parsing the message from a memory buffer when the buffer.
   * Incorporates a bug fix for a bug that was noted by Will Sullin of Callcast
   *
   * @param msgBuffer a byte buffer containing the messages to be parsed. This can consist of
   *     multiple SIP Messages concatenated together.
   * @return a SIPMessage[] structure (request or response) containing the parsed SIP message.
   * @exception ParseException is thrown when an illegal message has been encountered (and the rest
   *     of the buffer is discarded).
   * @see ParseExceptionListener
   */
  public SIPMessage parseSIPMessage(
      byte[] msgBuffer, boolean readBody, boolean strict, ParseExceptionListener exhandler)
      throws ParseException {
    if (msgBuffer == null || msgBuffer.length == 0) return null;

    int i = 0;

    // Squeeze out any leading control character.
    try {
      while (msgBuffer[i] < 0x20) i++;
    } catch (ArrayIndexOutOfBoundsException e) {
      // Array contains only control char, return null.
      return null;
    }

    // Iterate thru the request/status line and headers.
    char[] currentLine = null;
    char[] currentHeader = null;
    boolean isFirstLine = true;
    SIPMessage message = null;
    do {
      currentLine = null;
      int lineStart = i;

      // Find the length of the line.
      try {
        while (msgBuffer[i] != '\r' && msgBuffer[i] != '\n') i++;
      } catch (ArrayIndexOutOfBoundsException e) {
        // End of the message.
        break;
      }
      int lineLength = i - lineStart;

      ByteBuffer bb = ByteBuffer.wrap(msgBuffer, lineStart, lineLength);
      currentLine = charset.decode(bb).array();

      currentLine = trimEndOfLine(currentLine);

      if (currentLine.length == 0) {
        // Last header line, process the previous buffered header.
        if (currentHeader != null && message != null) {
          processHeader(currentHeader, message, exhandler, msgBuffer);
        }

      } else {
        if (isFirstLine) {
          message = processFirstLine(currentLine, exhandler, msgBuffer);
        } else {
          char firstChar = currentLine[0];
          if (firstChar == '\t' || firstChar == ' ') {
            if (currentHeader == null) throw new ParseException("Bad header continuation.", 0);

            // This is a continuation, append it to the previous line.
            //                        currentHeader += currentLine.substring(1);
            char[] retval = new char[currentHeader.length + currentLine.length - 1];
            System.arraycopy(currentHeader, 0, retval, 0, currentHeader.length);
            System.arraycopy(
                currentHeader, currentHeader.length, currentLine, 1, currentLine.length);
          } else {
            if (currentHeader != null && message != null) {
              processHeader(currentHeader, message, exhandler, msgBuffer);
            }
            currentHeader = new char[currentLine.length + 1];
            System.arraycopy(currentLine, 0, currentHeader, 0, currentLine.length);
            currentHeader[currentLine.length] = '\n';
          }
        }
      }

      if (msgBuffer[i] == '\r' && msgBuffer.length > i + 1 && msgBuffer[i + 1] == '\n') i++;

      i++;

      isFirstLine = false;
    } while (currentLine.length > 0); // End do - while

    currentLine = null;
    currentHeader = null;

    if (message == null) throw new ParseException("Bad message", 0);
    message.setSize(i);

    // Check for content legth header
    if (readBody && message.getContentLength() != null) {
      if (message.getContentLength().getContentLength() != 0) {
        int bodyLength = msgBuffer.length - i;

        byte[] body = new byte[bodyLength];
        System.arraycopy(msgBuffer, i, body, 0, bodyLength);
        message.setMessageContent(
            body,
            !strict,
            computeContentLengthFromMessage,
            message.getContentLength().getContentLength());
      } else if (!computeContentLengthFromMessage
          && message.getContentLength().getContentLength() == 0 & strict) {
        String last4Chars = new String(msgBuffer, msgBuffer.length - 4, 4);
        if (!"\r\n\r\n".equals(last4Chars)) {
          throw new ParseException("Extraneous characters at the end of the message ", i);
        }
      }
    }

    return message;
  }