content: /*
   * Conditions Of Use
   *
   * This software was developed by employees of the National Institute of
   * Standards and Technology (NIST), an agency of the Federal Government.
   * Pursuant to title 15 Untied States Code Section 105, works of NIST
   * employees are not subject to copyright protection in the United States
   * and are considered to be in the public domain.  As a result, a formal
   * license is not needed to use the software.
   *
   * This software is provided by NIST as a service and is expressly
   * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
   * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
   * AND DATA ACCURACY.  NIST does not warrant or make any representations
   * regarding the use of the software or the results thereof, including but
   * not limited to the correctness, accuracy, reliability or usefulness of
   * the software.
   *
   * Permission to use this software is contingent upon your acceptance
   * of the terms of this agreement
   *
   * .
   *
   */
  /******************************************************************************
   * Product of NIST/ITL Advanced Networking Technologies Division (ANTD)       *
   ******************************************************************************/
  package gov2.nist.javax2.sip.parser;
  
  /*
   *
   * Lamine Brahimi and Yann Duponchel (IBM Zurich) noticed that the parser was
   * blocking so I threw out some cool pipelining which ran fast but only worked
   * when the phase of the moon matched its mood. Now things are serialized and
   * life goes slower but more reliably.
   *
   */
  import gov2.nist.core.*;
  import gov2.nist.javax2.sip.header.*;
  import gov2.nist.javax2.sip.message.*;
  
  import java.text.ParseException;
  import java.io.*;
  
  /**
   * This implements a pipelined message parser suitable for use with a stream -
   * oriented input such as TCP. The client uses this class by instatiating with
   * an input stream from which input is read and fed to a message parser. It
   * keeps reading from the input stream and process messages in a never ending
   * interpreter loop. The message listener interface gets called for processing
   * messages or for processing errors. The payload specified by the
   * content-length header is read directly from the input stream. This can be
   * accessed from the SIPMessage using the getContent and getContentBytes methods
   * provided by the SIPMessage class.
   *
   * @version 1.2 $Revision: 1.23 $ $Date: 2009/08/16 17:28:28 $
   *
   * @author M. Ranganathan
   *
   * @see SIPMessageListener
   */
  public final class PipelinedMsgParser implements Runnable {
  
  
  
      /**
       * The message listener that is registered with this parser. (The message
       * listener has methods that can process correct and erroneous messages.)
       */
      protected SIPMessageListener sipMessageListener;
      private Thread mythread; // Preprocessor thread
      //private byte[] messageBody;
      //private boolean errorFlag;
      private Pipeline rawInputStream;
      private int maxMessageSize;
      private int sizeCounter;
      //private int messageSize;
  
      /**
       * default constructor.
       */
      protected PipelinedMsgParser() {
          super();
  
      }
  
      private static int uid = 0;
  
      private static synchronized int getNewUid() {
          return uid++;
      }
  
      /**
       * Constructor when we are given a message listener and an input stream
       * (could be a TCP connection or a file)
       *
       * @param sipMessageListener
       *            Message listener which has methods that get called back from
       *            the parser when a parse is complete
       * @param in
       *            Input stream from which to read the input.
       * @param debug
       *            Enable/disable tracing or lexical analyser switch.
       */
      public PipelinedMsgParser(SIPMessageListener sipMessageListener,
              Pipeline in, boolean debug, int maxMessageSize) {
          this();
          this.sipMessageListener = sipMessageListener;
          rawInputStream = in;
          this.maxMessageSize = maxMessageSize;
          mythread = new Thread(this);
          mythread.setName("PipelineThread-" + getNewUid());
  
      }
  
      /**
       * This is the constructor for the pipelined parser.
       *
       * @param mhandler
       *            a SIPMessageListener implementation that provides the message
       *            handlers to handle correctly and incorrectly parsed messages.
       * @param in
       *            An input stream to read messages from.
       */
  
      public PipelinedMsgParser(SIPMessageListener mhandler, Pipeline in,
              int maxMsgSize) {
          this(mhandler, in, false, maxMsgSize);
      }
  
      /**
       * This is the constructor for the pipelined parser.
       *
       * @param in -
       *            An input stream to read messages from.
       */
  
      public PipelinedMsgParser(Pipeline in) {
          this(null, in, false, 0);
      }
  
      /**
       * Start reading and processing input.
       */
      public void processInput() {
          mythread.start();
      }
  
      /**
       * Create a new pipelined parser from an existing one.
       *
       * @return A new pipelined parser that reads from the same input stream.
       */
      protected Object clone() {
          PipelinedMsgParser p = new PipelinedMsgParser();
  
          p.rawInputStream = this.rawInputStream;
          p.sipMessageListener = this.sipMessageListener;
          Thread mythread = new Thread(p);
          mythread.setName("PipelineThread");
          return p;
      }
  
      /**
       * Add a class that implements a SIPMessageListener interface whose methods
       * get called * on successful parse and error conditons.
       *
       * @param mlistener
       *            a SIPMessageListener implementation that can react to correct
       *            and incorrect pars.
       */
  
      public void setMessageListener(SIPMessageListener mlistener) {
          sipMessageListener = mlistener;
      }
  
      /**
       * read a line of input (I cannot use buffered reader because we may need to
       * switch encodings mid-stream!
       */
      private String readLine(InputStream inputStream) throws IOException {
          StringBuffer retval = new StringBuffer("");
          while (true) {
              char ch;
              int i = inputStream.read();
              if (i == -1) {
                  throw new IOException("End of stream");
              } else
                  ch = (char) i;
              // reduce the available read size by 1 ("size" of a char).
              if (this.maxMessageSize > 0) {
                  this.sizeCounter--;
                  if (this.sizeCounter <= 0)
                      throw new IOException("Max size exceeded!");
              }
              if (ch != '\r')
                  retval.append(ch);
              if (ch == '\n') {
                  break;
              }
          }
          return retval.toString();
      }
  
      /**
       * 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 blank line. ");
                              }
                              continue;
                          } else
                              break;
                      } catch (IOException ex) {
                          Debug.printStackTrace(ex);
                          this.rawInputStream.stopTimer();
                          return;
  
                      }
                  }
  
                  inputBuffer.append(line1);
                  // Guard against bad guys.
                  this.rawInputStream.startTimer();
  
                  Debug.println("Reading Input Stream");
                  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 {
                      if (Debug.debug) {
                          Debug.println("About to parse : " + inputBuffer.toString());
                      }
                      sipMessage = smp.parseSIPMessage(inputBuffer.toString());
                      if (sipMessage == null) {
                          this.rawInputStream.stopTimer();
                          continue;
                      }
                  } catch (ParseException ex) {
                      // Just ignore the parse exception.
                      Debug.logError("Detected a parse error", ex);
                      continue;
                  }
  
                  if (Debug.debug) {
                      Debug.println("Completed parsing message");
                  }
                  ContentLength cl = (ContentLength) sipMessage
                          .getContentLength();
                  int contentLength = 0;
                  if (cl != null) {
                      contentLength = cl.getContentLength();
                  } else {
                      contentLength = 0;
                  }
  
                  if (Debug.debug) {
                      Debug.println("contentLength " + contentLength);
                  }
  
                  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) {
                              Debug.logError("Exception Reading Content",ex);
                              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);
              }
          }
      }
  
      public void close() {
          try {
              this.rawInputStream.close();
          } catch (IOException ex) {
              // Ignore.
          }
      }
  }
  /*
   * $Log: PipelinedMsgParser.java,v $
   * Revision 1.23  2009/08/16 17:28:28  mranga
   * Issue number:  208
   * Obtained from:
   * Submitted by:
   * Reviewed by:
   *
   * Add authentication mechanism that uses H(username:domain:password)
   *
   * Revision 1.22  2009/07/17 18:58:02  emcho
   * Converts indentation tabs to spaces so that we have a uniform indentation policy in the whole project.
   *
   * Revision 1.21  2008/05/24 04:10:01  mranga
   *
   * Issue number:   158
   * Obtained from:
   * Submitted by:
   * Reviewed by:   mranga
   *
   * Deliver tx timeout for Canceled INVITE. Fix pipeline thread exit.
   *
   * Revision 1.20  2008/05/22 19:38:07  jbemmel
   * Fix for issue 149: the logic wasn't always closing the internal socket pipe,
   * causing the pipe reader thread to block indefinitely
   *
   * Repeatedly starting/stopping the stack then gives hanging threads
   * Revision 1.19 2007/01/28 13:06:21 mranga
   * Issue number: 99 Obtained from: Submitted by: Reviewed by: mranga
   *
   * Fixed PRACK handling null pointer exception (for proxy case) and cleanup of
   * unused variables.
   *
   * CVS: ----------------------------------------------------------------------
   * CVS: Issue number: CVS: If this change addresses one or more issues, CVS:
   * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change
   * has been taken from another system, CVS: then name the system in this line,
   * otherwise delete it. CVS: Submitted by: CVS: If this code has been
   * contributed to the project by someone else; i.e., CVS: they sent us a patch
   * or a set of diffs, then include their name/email CVS: address here. If this
   * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing
   * pre-commit code reviews and someone else has CVS: reviewed your changes,
   * include their name(s) here. CVS: If you have not had it reviewed then delete
   * this line.
   *
   * Revision 1.18 2006/07/13 09:02:10 mranga Issue number: Obtained from:
   * Submitted by: jeroen van bemmel Reviewed by: mranga Moved some changes from
   * jain-sip-1.2 to java.net
   *
   * CVS: ----------------------------------------------------------------------
   * CVS: Issue number: CVS: If this change addresses one or more issues, CVS:
   * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change
   * has been taken from another system, CVS: then name the system in this line,
   * otherwise delete it. CVS: Submitted by: CVS: If this code has been
   * contributed to the project by someone else; i.e., CVS: they sent us a patch
   * or a set of diffs, then include their name/email CVS: address here. If this
   * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing
   * pre-commit code reviews and someone else has CVS: reviewed your changes,
   * include their name(s) here. CVS: If you have not had it reviewed then delete
   * this line.
   *
   * Revision 1.4 2006/06/19 06:47:27 mranga javadoc fixups
   *
   * Revision 1.3 2006/06/17 10:18:14 mranga Added some synchronization to the
   * sequence number checking. Small javadoc fixups
   *
   * Revision 1.2 2006/06/16 15:26:28 mranga Added NIST disclaimer to all public
   * domain files. Clean up some javadoc. Fixed a leak
   *
   * Revision 1.1.1.1 2005/10/04 17:12:35 mranga
   *
   * Import
   *
   *
   * Revision 1.16 2004/11/30 23:28:14 mranga Issue number: 44 Submitted by: Rob
   * Daugherty Reviewed by: M. Ranganathan
   *
   * TCP Pipelining truncates content when other end of pipe is closed.
   *
   * Revision 1.15 2004/05/30 18:55:56 mranga Reviewed by: mranga Move to timers
   * and eliminate the Transaction scanner Thread to improve scalability and
   * reduce cpu usage.
   *
   * Revision 1.14 2004/05/16 14:13:22 mranga Reviewed by: mranga Fixed the
   * use-count issue reported by Peter Parnes. Added property to prevent against
   * content-length dos attacks.
   *
   * Revision 1.13 2004/03/19 04:22:22 mranga Reviewed by: mranga Added IO Pacing
   * for long writes - split write into chunks and flush after each chunk to avoid
   * socket back pressure.
   *
   * Revision 1.12 2004/03/18 22:01:19 mranga Reviewed by: mranga Get rid of the
   * PipedInputStream from pipelined parser to avoid a copy.
   *
   * Revision 1.11 2004/03/07 22:25:23 mranga Reviewed by: mranga Added a new
   * configuration parameter that instructs the stack to drop a server connection
   * after server transaction termination set
   * gov2.nist.javax2.sip.CACHE_SERVER_CONNECTIONS=false for this Default behavior
   * is true.
   *
   * Revision 1.10 2004/02/29 15:32:58 mranga Reviewed by: mranga bug fixes on
   * limiting the max message size.
   *
   * Revision 1.9 2004/02/29 00:46:34 mranga Reviewed by: mranga Added new
   * configuration property to limit max message size for TCP transport. The
   * property is gov2.nist.javax2.sip.MAX_MESSAGE_SIZE
   *
   * Revision 1.8 2004/02/25 21:43:03 mranga Reviewed by: mranga Added a couple of
   * todo's and removed some debug printlns that could slow code down by a bit.
   *
   * Revision 1.7 2004/02/25 20:52:46 mranga Reviewed by: mranga Fix TCP transport
   * so messages in excess of 8192 bytes are accepted.
   *
   * Revision 1.6 2004/01/22 18:39:41 mranga Reviewed by: M. Ranganathan Moved the
   * ifdef SIMULATION and associated tags to the first column so Prep preprocessor
   * can deal with them.
   *
   * Revision 1.5 2004/01/22 14:23:45 mranga Reviewed by: mranga Fixed some minor
   * formatting issues.
   *
   * Revision 1.4 2004/01/22 13:26:31 sverker Issue number: Obtained from:
   * Submitted by: sverker Reviewed by: mranga
   *
   * Major reformat of code to conform with style guide. Resolved compiler and
   * javadoc warnings. Added CVS tags.
   *
   * CVS: ----------------------------------------------------------------------
   * CVS: Issue number: CVS: If this change addresses one or more issues, CVS:
   * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change
   * has been taken from another system, CVS: then name the system in this line,
   * otherwise delete it. CVS: Submitted by: CVS: If this code has been
   * contributed to the project by someone else; i.e., CVS: they sent us a patch
   * or a set of diffs, then include their name/email CVS: address here. If this
   * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing
   * pre-commit code reviews and someone else has CVS: reviewed your changes,
   * include their name(s) here. CVS: If you have not had it reviewed then delete
   * this line.
   *
   */
  