Home | History | Annotate | Download | only in parser
      1 /*
      2  * Conditions Of Use
      3  *
      4  * This software was developed by employees of the National Institute of
      5  * Standards and Technology (NIST), an agency of the Federal Government.
      6  * Pursuant to title 15 Untied States Code Section 105, works of NIST
      7  * employees are not subject to copyright protection in the United States
      8  * and are considered to be in the public domain.  As a result, a formal
      9  * license is not needed to use the software.
     10  *
     11  * This software is provided by NIST as a service and is expressly
     12  * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
     13  * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
     14  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
     15  * AND DATA ACCURACY.  NIST does not warrant or make any representations
     16  * regarding the use of the software or the results thereof, including but
     17  * not limited to the correctness, accuracy, reliability or usefulness of
     18  * the software.
     19  *
     20  * Permission to use this software is contingent upon your acceptance
     21  * of the terms of this agreement
     22  *
     23  * .
     24  *
     25  */
     26 
     27 /*******************************************************************************
     28  * Product of NIST/ITL Advanced Networking Technologies Division (ANTD)        *
     29  ******************************************************************************/
     30 
     31 package gov.nist.javax.sip.parser;
     32 
     33 import gov.nist.core.Host;
     34 import gov.nist.core.HostNameParser;
     35 import gov.nist.javax.sip.SIPConstants;
     36 import gov.nist.javax.sip.address.AddressImpl;
     37 import gov.nist.javax.sip.address.GenericURI;
     38 import gov.nist.javax.sip.address.SipUri;
     39 import gov.nist.javax.sip.address.TelephoneNumber;
     40 import gov.nist.javax.sip.header.*;
     41 import gov.nist.javax.sip.message.SIPMessage;
     42 import gov.nist.javax.sip.message.SIPRequest;
     43 import gov.nist.javax.sip.message.SIPResponse;
     44 
     45 import java.io.UnsupportedEncodingException;
     46 import java.text.ParseException;
     47 /*
     48  * Acknowledgement: 1/12/2007: Yanick Belanger rewrote the parsing loops to make them
     49  * simpler and quicker.
     50  */
     51 
     52 /**
     53  * Parse SIP message and parts of SIP messages such as URI's etc from memory and
     54  * return a structure. Intended use: UDP message processing. This class is used
     55  * when you have an entire SIP message or SIPHeader or SIP URL in memory and you
     56  * want to generate a parsed structure from it. For SIP messages, the payload
     57  * can be binary or String. If you have a binary payload, use
     58  * parseSIPMessage(byte[]) else use parseSIPMessage(String) The payload is
     59  * accessible from the parsed message using the getContent and getContentBytes
     60  * methods provided by the SIPMessage class. If SDP parsing is enabled using the
     61  * parseContent method, then the SDP body is also parsed and can be accessed
     62  * from the message using the getSDPAnnounce method. Currently only eager
     63  * parsing of the message is supported (i.e. the entire message is parsed in one
     64  * feld swoop).
     65  *
     66  *
     67  * @version 1.2 $Revision: 1.26 $ $Date: 2009/10/22 10:27:38 $
     68  *
     69  * @author M. Ranganathan <br/>
     70  *
     71   *
     72  */
     73 public class StringMsgParser {
     74 
     75     protected boolean readBody;
     76     private ParseExceptionListener parseExceptionListener;
     77     private String rawStringMessage;
     78     private boolean strict;
     79 
     80     private static boolean computeContentLengthFromMessage = false;
     81 
     82     /**
     83      * @since v0.9
     84      */
     85     public StringMsgParser() {
     86         super();
     87         readBody = true;
     88     }
     89 
     90     /**
     91      * Constructor (given a parse exception handler).
     92      *
     93      * @since 1.0
     94      * @param exhandler
     95      *            is the parse exception listener for the message parser.
     96      */
     97     public StringMsgParser(ParseExceptionListener exhandler) {
     98         this();
     99         parseExceptionListener = exhandler;
    100     }
    101 
    102     /**
    103      * Add a handler for header parsing errors.
    104      *
    105      * @param pexhandler
    106      *            is a class that implements the ParseExceptionListener
    107      *            interface.
    108      */
    109     public void setParseExceptionListener(ParseExceptionListener pexhandler) {
    110         parseExceptionListener = pexhandler;
    111     }
    112 
    113     /**
    114      * Parse a buffer containing a single SIP Message where the body is an array
    115      * of un-interpreted bytes. This is intended for parsing the message from a
    116      * memory buffer when the buffer. Incorporates a bug fix for a bug that was
    117      * noted by Will Sullin of Callcast
    118      *
    119      * @param msgBuffer
    120      *            a byte buffer containing the messages to be parsed. This can
    121      *            consist of multiple SIP Messages concatenated together.
    122      * @return a SIPMessage[] structure (request or response) containing the
    123      *         parsed SIP message.
    124      * @exception ParseException
    125      *                is thrown when an illegal message has been encountered
    126      *                (and the rest of the buffer is discarded).
    127      * @see ParseExceptionListener
    128      */
    129     public SIPMessage parseSIPMessage(byte[] msgBuffer) throws ParseException {
    130         if (msgBuffer == null || msgBuffer.length == 0)
    131             return null;
    132 
    133         int i = 0;
    134 
    135         // Squeeze out any leading control character.
    136         try {
    137             while (msgBuffer[i] < 0x20)
    138                 i++;
    139         }
    140         catch (ArrayIndexOutOfBoundsException e) {
    141             // Array contains only control char, return null.
    142             return null;
    143         }
    144 
    145         // Iterate thru the request/status line and headers.
    146         String currentLine = null;
    147         String currentHeader = null;
    148         boolean isFirstLine = true;
    149         SIPMessage message = null;
    150         do
    151         {
    152             int lineStart = i;
    153 
    154             // Find the length of the line.
    155             try {
    156                 while (msgBuffer[i] != '\r' && msgBuffer[i] != '\n')
    157                     i++;
    158             }
    159             catch (ArrayIndexOutOfBoundsException e) {
    160                 // End of the message.
    161                 break;
    162             }
    163             int lineLength = i - lineStart;
    164 
    165             // Make it a String.
    166             try {
    167                 currentLine = new String(msgBuffer, lineStart, lineLength, "UTF-8");
    168             } catch (UnsupportedEncodingException e) {
    169                 throw new ParseException("Bad message encoding!", 0);
    170             }
    171 
    172             currentLine = trimEndOfLine(currentLine);
    173 
    174             if (currentLine.length() == 0) {
    175                 // Last header line, process the previous buffered header.
    176                 if (currentHeader != null && message != null) {
    177                      processHeader(currentHeader, message);
    178                  }
    179 
    180             }
    181             else {
    182                 if (isFirstLine) {
    183                     message = processFirstLine(currentLine);
    184                 } else {
    185                     char firstChar = currentLine.charAt(0);
    186                     if (firstChar == '\t' || firstChar == ' ') {
    187                         if (currentHeader == null)
    188                             throw new ParseException("Bad header continuation.", 0);
    189 
    190                         // This is a continuation, append it to the previous line.
    191                         currentHeader += currentLine.substring(1);
    192                     }
    193                     else {
    194                         if (currentHeader != null && message != null) {
    195                              processHeader(currentHeader, message);
    196                          }
    197                         currentHeader = currentLine;
    198                     }
    199                 }
    200             }
    201 
    202             if (msgBuffer[i] == '\r' && msgBuffer.length > i+1 && msgBuffer[i+1] == '\n')
    203                 i++;
    204 
    205             i++;
    206 
    207             isFirstLine = false;
    208         } while (currentLine.length() > 0); // End do - while
    209 
    210         if (message == null) throw new ParseException("Bad message", 0);
    211         message.setSize(i);
    212 
    213         if (readBody && message.getContentLength() != null &&
    214                 message.getContentLength().getContentLength() != 0) {
    215 
    216             int bodyLength = msgBuffer.length - i;
    217 
    218             byte[] body = new byte[bodyLength];
    219             System.arraycopy(msgBuffer, i, body, 0, bodyLength);
    220             message.setMessageContent(body,computeContentLengthFromMessage ,message.getContentLength().getContentLength() );
    221         }
    222 
    223         return message;
    224     }
    225 
    226     /**
    227      * Parse a buffer containing one or more SIP Messages and return an array of
    228      * SIPMessage parsed structures.
    229      *
    230      * @param msgString
    231      *            a String containing the messages to be parsed. This can
    232      *            consist of multiple SIP Messages concatenated together.
    233      * @return a SIPMessage structure (request or response) containing the
    234      *         parsed SIP message.
    235      * @exception ParseException
    236      *                is thrown when an illegal message has been encountered
    237      *                (and the rest of the buffer is discarded).
    238      * @see ParseExceptionListener
    239      */
    240     public SIPMessage parseSIPMessage(String msgString) throws ParseException {
    241         if (msgString == null || msgString.length() == 0)
    242             return null;
    243 
    244         rawStringMessage = msgString;
    245 
    246         int i = 0;
    247 
    248         // Squeeze out any leading control character.
    249         try {
    250             while (msgString.charAt(i) < 0x20)
    251                 i++;
    252         }
    253         catch (ArrayIndexOutOfBoundsException e) {
    254             // Array contains only control char, return null.
    255             return null;
    256         } catch (StringIndexOutOfBoundsException ex) {
    257             return null;
    258         }
    259 
    260         // Iterate thru the request/status line and headers.
    261         String currentLine = null;
    262         String currentHeader = null;
    263         boolean isFirstLine = true;
    264         SIPMessage message = null;
    265         do
    266         {
    267             int lineStart = i;
    268 
    269             // Find the length of the line.
    270             try {
    271                 char c = msgString.charAt(i);
    272                 while (c != '\r' && c != '\n')
    273                     c = msgString.charAt(++i);
    274             }
    275             catch (ArrayIndexOutOfBoundsException e) {
    276                 // End of the message.
    277                 break;
    278             } catch ( StringIndexOutOfBoundsException ex) {
    279                 break;
    280             }
    281 
    282             // Make it a String.
    283             currentLine = msgString.substring(lineStart, i);
    284             currentLine = trimEndOfLine(currentLine);
    285 
    286             if (currentLine.length() == 0) {
    287                 // Last header line, process the previous buffered header.
    288                 if (currentHeader != null) {
    289                     processHeader(currentHeader, message);
    290                 }
    291             }
    292             else {
    293                 if (isFirstLine) {
    294                     message = processFirstLine(currentLine);
    295                 } else {
    296                     char firstChar = currentLine.charAt(0);
    297                     if (firstChar == '\t' || firstChar == ' ') {
    298                         if (currentHeader == null)
    299                             throw new ParseException("Bad header continuation.", 0);
    300 
    301                         // This is a continuation, append it to the previous line.
    302                         currentHeader += currentLine.substring(1);
    303                     }
    304                     else {
    305                         if (currentHeader != null) {
    306                             processHeader(currentHeader, message);
    307                         }
    308                         currentHeader = currentLine;
    309                     }
    310                 }
    311             }
    312 
    313             if (msgString.charAt(i) == '\r' && msgString.length() > i+1 && msgString.charAt(i+1) == '\n')
    314                 i++;
    315 
    316             i++;
    317 
    318             isFirstLine = false;
    319         }
    320         while (currentLine.length() > 0);
    321 
    322         message.setSize(i);
    323 
    324         // Check for content legth header
    325         if (readBody && message.getContentLength() != null ) {
    326             if ( message.getContentLength().getContentLength() != 0) {
    327                 String body = msgString.substring(i);
    328                 message.setMessageContent(body,this.strict,computeContentLengthFromMessage,message.getContentLength().getContentLength());
    329              } else if (!computeContentLengthFromMessage && message.getContentLength().getContentLength() == 0 && !msgString.endsWith("\r\n\r\n") ){
    330                  if ( strict ) {
    331                      throw new ParseException("Extraneous characters at the end of the message ",i);
    332                  }
    333              }
    334 
    335         }
    336 
    337         return message;
    338     }
    339 
    340     private String trimEndOfLine(String line) {
    341         if (line == null)
    342             return line;
    343 
    344         int i = line.length() - 1;
    345         while (i >= 0 && line.charAt(i) <= 0x20)
    346             i--;
    347 
    348         if (i == line.length() - 1)
    349             return line;
    350 
    351         if (i == -1)
    352             return "";
    353 
    354         return line.substring(0, i+1);
    355     }
    356 
    357     private SIPMessage processFirstLine(String firstLine) throws ParseException {
    358         SIPMessage message;
    359         if (!firstLine.startsWith(SIPConstants.SIP_VERSION_STRING)) {
    360             message = new SIPRequest();
    361             try {
    362                 RequestLine requestLine = new RequestLineParser(firstLine + "\n")
    363                         .parse();
    364                 ((SIPRequest) message).setRequestLine(requestLine);
    365             } catch (ParseException ex) {
    366                 if (this.parseExceptionListener != null)
    367                     this.parseExceptionListener.handleException(ex, message,
    368                             RequestLine.class, firstLine, rawStringMessage);
    369                 else
    370                     throw ex;
    371 
    372             }
    373         } else {
    374             message = new SIPResponse();
    375             try {
    376                 StatusLine sl = new StatusLineParser(firstLine + "\n").parse();
    377                 ((SIPResponse) message).setStatusLine(sl);
    378             } catch (ParseException ex) {
    379                 if (this.parseExceptionListener != null) {
    380                     this.parseExceptionListener.handleException(ex, message,
    381                             StatusLine.class, firstLine, rawStringMessage);
    382                 } else
    383                     throw ex;
    384 
    385             }
    386         }
    387         return message;
    388     }
    389 
    390     private void processHeader(String header, SIPMessage message) throws ParseException {
    391         if (header == null || header.length() == 0)
    392             return;
    393 
    394         HeaderParser headerParser = null;
    395         try {
    396             headerParser = ParserFactory.createParser(header + "\n");
    397         } catch (ParseException ex) {
    398             this.parseExceptionListener.handleException(ex, message, null,
    399                     header, rawStringMessage);
    400             return;
    401         }
    402 
    403         try {
    404             SIPHeader sipHeader = headerParser.parse();
    405             message.attachHeader(sipHeader, false);
    406         } catch (ParseException ex) {
    407             if (this.parseExceptionListener != null) {
    408                 String headerName = Lexer.getHeaderName(header);
    409                 Class headerClass = NameMap.getClassFromName(headerName);
    410                 if (headerClass == null) {
    411                     headerClass = ExtensionHeaderImpl.class;
    412 
    413                 }
    414                 this.parseExceptionListener.handleException(ex, message,
    415                         headerClass, header, rawStringMessage);
    416 
    417             }
    418         }
    419     }
    420 
    421     /**
    422      * Parse an address (nameaddr or address spec) and return and address
    423      * structure.
    424      *
    425      * @param address
    426      *            is a String containing the address to be parsed.
    427      * @return a parsed address structure.
    428      * @since v1.0
    429      * @exception ParseException
    430      *                when the address is badly formatted.
    431      */
    432     public AddressImpl parseAddress(String address) throws ParseException {
    433         AddressParser addressParser = new AddressParser(address);
    434         return addressParser.address(true);
    435     }
    436 
    437     /**
    438      * Parse a host:port and return a parsed structure.
    439      *
    440      * @param hostport
    441      *            is a String containing the host:port to be parsed
    442      * @return a parsed address structure.
    443      * @since v1.0
    444      * @exception throws
    445      *                a ParseException when the address is badly formatted.
    446      *
    447     public HostPort parseHostPort(String hostport) throws ParseException {
    448         Lexer lexer = new Lexer("charLexer", hostport);
    449         return new HostNameParser(lexer).hostPort();
    450 
    451     }
    452     */
    453 
    454     /**
    455      * Parse a host name and return a parsed structure.
    456      *
    457      * @param host
    458      *            is a String containing the host name to be parsed
    459      * @return a parsed address structure.
    460      * @since v1.0
    461      * @exception ParseException
    462      *                a ParseException when the hostname is badly formatted.
    463      */
    464     public Host parseHost(String host) throws ParseException {
    465         Lexer lexer = new Lexer("charLexer", host);
    466         return new HostNameParser(lexer).host();
    467 
    468     }
    469 
    470     /**
    471      * Parse a telephone number return a parsed structure.
    472      *
    473      * @param telephone_number
    474      *            is a String containing the telephone # to be parsed
    475      * @return a parsed address structure.
    476      * @since v1.0
    477      * @exception ParseException
    478      *                a ParseException when the address is badly formatted.
    479      */
    480     public TelephoneNumber parseTelephoneNumber(String telephone_number)
    481             throws ParseException {
    482         // Bug fix contributed by Will Scullin
    483         return new URLParser(telephone_number).parseTelephoneNumber(true);
    484 
    485     }
    486 
    487     /**
    488      * Parse a SIP url from a string and return a URI structure for it.
    489      *
    490      * @param url
    491      *            a String containing the URI structure to be parsed.
    492      * @return A parsed URI structure
    493      * @exception ParseException
    494      *                if there was an error parsing the message.
    495      */
    496 
    497     public SipUri parseSIPUrl(String url) throws ParseException {
    498         try {
    499             return new URLParser(url).sipURL(true);
    500         } catch (ClassCastException ex) {
    501             throw new ParseException(url + " Not a SIP URL ", 0);
    502         }
    503     }
    504 
    505     /**
    506      * Parse a uri from a string and return a URI structure for it.
    507      *
    508      * @param url
    509      *            a String containing the URI structure to be parsed.
    510      * @return A parsed URI structure
    511      * @exception ParseException
    512      *                if there was an error parsing the message.
    513      */
    514 
    515     public GenericURI parseUrl(String url) throws ParseException {
    516         return new URLParser(url).parse();
    517     }
    518 
    519     /**
    520      * Parse an individual SIP message header from a string.
    521      *
    522      * @param header
    523      *            String containing the SIP header.
    524      * @return a SIPHeader structure.
    525      * @exception ParseException
    526      *                if there was an error parsing the message.
    527      */
    528     public SIPHeader parseSIPHeader(String header) throws ParseException {
    529         int start = 0;
    530         int end = header.length() - 1;
    531         try {
    532             // Squeeze out any leading control character.
    533             while (header.charAt(start) <= 0x20)
    534                 start++;
    535 
    536             // Squeeze out any trailing control character.
    537             while (header.charAt(end) <= 0x20)
    538                 end--;
    539         }
    540         catch (ArrayIndexOutOfBoundsException e) {
    541             // Array contains only control char.
    542             throw new ParseException("Empty header.", 0);
    543         }
    544 
    545         StringBuffer buffer = new StringBuffer(end + 1);
    546         int i = start;
    547         int lineStart = start;
    548         boolean endOfLine = false;
    549         while (i <= end) {
    550             char c = header.charAt(i);
    551             if (c == '\r' || c == '\n') {
    552                 if (!endOfLine) {
    553                     buffer.append(header.substring(lineStart, i));
    554                     endOfLine = true;
    555                 }
    556             }
    557             else {
    558                 if (endOfLine) {
    559                     endOfLine = false;
    560                     if (c == ' ' || c == '\t') {
    561                         buffer.append(' ');
    562                         lineStart = i + 1;
    563                     }
    564                     else {
    565                         lineStart = i;
    566                     }
    567                 }
    568             }
    569 
    570             i++;
    571         }
    572         buffer.append(header.substring(lineStart, i));
    573         buffer.append('\n');
    574 
    575         HeaderParser hp = ParserFactory.createParser(buffer.toString());
    576         if (hp == null)
    577             throw new ParseException("could not create parser", 0);
    578         return hp.parse();
    579     }
    580 
    581     /**
    582      * Parse the SIP Request Line
    583      *
    584      * @param requestLine
    585      *            a String containing the request line to be parsed.
    586      * @return a RequestLine structure that has the parsed RequestLine
    587      * @exception ParseException
    588      *                if there was an error parsing the requestLine.
    589      */
    590 
    591     public RequestLine parseSIPRequestLine(String requestLine)
    592             throws ParseException {
    593         requestLine += "\n";
    594         return new RequestLineParser(requestLine).parse();
    595     }
    596 
    597     /**
    598      * Parse the SIP Response message status line
    599      *
    600      * @param statusLine
    601      *            a String containing the Status line to be parsed.
    602      * @return StatusLine class corresponding to message
    603      * @exception ParseException
    604      *                if there was an error parsing
    605      * @see StatusLine
    606      */
    607 
    608     public StatusLine parseSIPStatusLine(String statusLine)
    609             throws ParseException {
    610         statusLine += "\n";
    611         return new StatusLineParser(statusLine).parse();
    612     }
    613 
    614     public static void setComputeContentLengthFromMessage(
    615             boolean computeContentLengthFromMessage) {
    616         StringMsgParser.computeContentLengthFromMessage = computeContentLengthFromMessage;
    617     }
    618 
    619 
    620 
    621     /**
    622      * Test code.
    623      */
    624     public static void main(String[] args) throws ParseException {
    625         String messages[] = {
    626                 "SIP/2.0 200 OK\r\n"
    627                         + "To: \"The Little Blister\" <sip:LittleGuy (at) there.com>;tag=469bc066\r\n"
    628                         + "From: \"The Master Blaster\" <sip:BigGuy (at) here.com>;tag=11\r\n"
    629                         + "Via: SIP/2.0/UDP 139.10.134.246:5060;branch=z9hG4bK8b0a86f6_1030c7d18e0_17;received=139.10.134.246\r\n"
    630                         + "Call-ID: 1030c7d18ae_a97b0b_b@8b0a86f6\r\n"
    631                         + "CSeq: 1 SUBSCRIBE\r\n"
    632                         + "Contact: <sip:172.16.11.162:5070>\r\n"
    633                         + "Content-Length: 0\r\n\r\n",
    634 
    635                 "SIP/2.0 180 Ringing\r\n"
    636                         + "Via: SIP/2.0/UDP 172.18.1.29:5060;branch=z9hG4bK43fc10fb4446d55fc5c8f969607991f4\r\n"
    637                         + "To: \"0440\" <sip:0440 (at) 212.209.220.131>;tag=2600\r\n"
    638                         + "From: \"Andreas\" <sip:andreas (at) e-horizon.se>;tag=8524\r\n"
    639                         + "Call-ID: f51a1851c5f570606140f14c8eb64fd3 (at) 172.18.1.29\r\n"
    640                         + "CSeq: 1 INVITE\r\n" + "Max-Forwards: 70\r\n"
    641                         + "Record-Route: <sip:212.209.220.131:5060>\r\n"
    642                         + "Content-Length: 0\r\n\r\n",
    643                 "REGISTER sip:nist.gov SIP/2.0\r\n"
    644                         + "Via: SIP/2.0/UDP 129.6.55.182:14826\r\n"
    645                         + "Max-Forwards: 70\r\n"
    646                         + "From: <sip:mranga (at) nist.gov>;tag=6fcd5c7ace8b4a45acf0f0cd539b168b;epid=0d4c418ddf\r\n"
    647                         + "To: <sip:mranga (at) nist.gov>\r\n"
    648                         + "Call-ID: c5679907eb954a8da9f9dceb282d7230 (at) 129.6.55.182\r\n"
    649                         + "CSeq: 1 REGISTER\r\n"
    650                         + "Contact: <sip:129.6.55.182:14826>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER\"\r\n"
    651                         + "User-Agent: RTC/(Microsoft RTC)\r\n"
    652                         + "Event:  registration\r\n"
    653                         + "Allow-Events: presence\r\n"
    654                         + "Content-Length: 0\r\n\r\n"
    655                         + "INVITE sip:littleguy (at) there.com:5060 SIP/2.0\r\n"
    656                         + "Via: SIP/2.0/UDP 65.243.118.100:5050\r\n"
    657                         + "From: M. Ranganathan  <sip:M.Ranganathan (at) sipbakeoff.com>;tag=1234\r\n"
    658                         + "To: \"littleguy (at) there.com\" <sip:littleguy (at) there.com:5060> \r\n"
    659                         + "Call-ID: Q2AboBsaGn9!?x6 (at) sipbakeoff.com \r\n"
    660                         + "CSeq: 1 INVITE \r\n"
    661                         + "Content-Length: 247\r\n\r\n"
    662                         + "v=0\r\n"
    663                         + "o=4855 13760799956958020 13760799956958020 IN IP4  129.6.55.78\r\n"
    664                         + "s=mysession session\r\n" + "p=+46 8 52018010\r\n"
    665                         + "c=IN IP4  129.6.55.78\r\n" + "t=0 0\r\n"
    666                         + "m=audio 6022 RTP/AVP 0 4 18\r\n"
    667                         + "a=rtpmap:0 PCMU/8000\r\n"
    668                         + "a=rtpmap:4 G723/8000\r\n"
    669                         + "a=rtpmap:18 G729A/8000\r\n" + "a=ptime:20\r\n" };
    670 
    671         class ParserThread implements Runnable {
    672             String[] messages;
    673 
    674             public ParserThread(String[] messagesToParse) {
    675                 this.messages = messagesToParse;
    676             }
    677 
    678             public void run() {
    679                 for (int i = 0; i < messages.length; i++) {
    680                     StringMsgParser smp = new StringMsgParser();
    681                     try {
    682                         SIPMessage sipMessage = smp
    683                                 .parseSIPMessage(messages[i]);
    684                         System.out.println(" i = " + i + " branchId = "
    685                                 + sipMessage.getTopmostVia().getBranch());
    686                         // System.out.println("encoded " +
    687                         // sipMessage.toString());
    688                     } catch (ParseException ex) {
    689 
    690                     }
    691 
    692                     // System.out.println("dialog id = " +
    693                     // sipMessage.getDialogId(false));
    694                 }
    695             }
    696         }
    697 
    698         for (int i = 0; i < 20; i++) {
    699             new Thread(new ParserThread(messages)).start();
    700         }
    701 
    702     }
    703 
    704     public void setStrict(boolean strict) {
    705        this.strict = strict;
    706 
    707     }
    708 
    709 }
    710