FileDocCategorySizeDatePackage
StringMsgParser.javaAPI DocphoneME MR2 API (J2ME)26233Wed May 02 18:00:42 BST 2007gov.nist.siplite.parser

StringMsgParser

public class StringMsgParser extends Object
Parse SIP message and parts of SIP messages such as URI's etc from memory and return a structure. Intended use: UDP message processing. This class is used when you have an entire SIP message or Header or SIP URL in memory and you want to generate a parsed structure from it. For SIP messages, the payload can be binary or String. If you have a binary payload, use parseMessage(byte[]) else use parseSIPMessage(String) The payload is accessible from the parsed message using the getContent and getContentBytes methods provided by the Message class. Currently only eager parsing of the message is supported (i.e. the entire message is parsed in one feld swoop).
version
JAIN-SIP-1.1 This code is in the public domain.

Fields Summary
protected boolean
readBody
Flag indicating body read requested.
private String
rawMessage
Unprocessed message part 1 (for error reporting).
private String
rawMessage1
Unprocessed message part 2 (for error reporting).
private String
currentMessage
Current message.
private ParseExceptionListener
parseExceptionListener
Parsing exeception listener.
private Vector
messageHeaders
Message headers.
private int
bufferPointer
Current buffer pointer.
private boolean
bodyIsString
Flag indicating bodyis a text string.
private byte[]
currentMessageBytes
Current message contents as an arrayof bytes.
protected int
contentLength
Lengthg of current message body.
private boolean
debugFlag
Debugging enabled flag.
private int
currentLine
Current line being parsed.
private String
currentHeader
Current header being processed.
Constructors Summary
public StringMsgParser()
Default constructor.

        super();
        messageHeaders = new Vector(10, 10);
        bufferPointer = 0;
        currentLine = 0;
        readBody = true;
    
public StringMsgParser(ParseExceptionListener exhandler)
Constructor (given a parse exception handler).

since
1.0
param
exhandler is the parse exception listener for the message parser.

        this();
        parseExceptionListener = exhandler;
    
Methods Summary
protected byte[]getBodyAsBytes()
Gets the message body as a byte array.

return
the mesage body

        if (this.contentLength == 0) {
            return null;
        } else {
            int endIndex = bufferPointer + this.contentLength;
            // guard against bad specifications.
            if (endIndex > currentMessageBytes.length) {
                endIndex = currentMessageBytes.length;
            }
            byte[] body = new byte[endIndex - bufferPointer];
            System.arraycopy
                    (currentMessageBytes, bufferPointer, body, 0, body.length);
            bufferPointer = endIndex;
            this.contentLength = 0;
            return body;
        }

    
public java.lang.StringgetCurrentHeader()
Gets the current header.

return
the current header

        return currentHeader;
    
public intgetCurrentLineNumber()
Gets the current line number.

return
the current line number

        return currentLine;
    
protected java.lang.StringgetMessageBody()
Gets the message body.

return
the message body


        if (this.contentLength == 0) {
            return null;
        } else {
            int endIndex = bufferPointer + this.contentLength;
            String body;
            // guard against bad specifications.
            if (endIndex > currentMessage.length()) {
                endIndex = currentMessage.length();
                body = currentMessage.substring(bufferPointer, endIndex);
                bufferPointer = endIndex;
            } else {
                body = currentMessage.substring(bufferPointer, endIndex);
                bufferPointer = endIndex + 1;
            }
            this.contentLength = 0;
            return body;
        }

    
protected booleanisBodyString()
Returns true if the body is encoded as a string. If the parseMessage(String) method is invoked then the body is assumed to be a string.

return
true if body is a string

        return bodyIsString;
    
private java.lang.StringBuffernormalizeMessage(java.lang.StringBuffer srcMsg)
Normalize message string, i.e. remove whitespace

param
srcMsg message to be processed
return
normalized message

        StringBuffer normalizedMessage = new StringBuffer(srcMsg.length());
        String message1 = srcMsg.toString();
        int length = message1.indexOf("\n\n") + 2;
        int k = 0;
        while (k < length - 1) {
            final char thisChar = srcMsg.charAt(k);
            final char thatChar = srcMsg.charAt(k+1);

            // is it a continuation line?
            if (thisChar == '\n" && (thatChar == '\t" || thatChar == ' ")) {
                normalizedMessage.append(' ");
                k++; // skipping \n
                // now remove whitespace
                char nextChar;
                do {
                    k++; // skipping \t or space
                    if (k == length) {
                        break;
                    }
                    nextChar = srcMsg.charAt(k);
                } while (nextChar == ' " || nextChar == '\t");
            } else {
                normalizedMessage.append(thisChar);
                k++;
            }
        }
        return normalizedMessage;
    
public AddressparseAddress(java.lang.String address)
Parses an address (nameaddr or address spec) and return and address structure.

param
address is a String containing the address to be parsed.
return
a parsed address structure.
since
v1.0
exception
ParseException when the address is badly formatted.

        AddressParser addressParser = new AddressParser(address);
        return addressParser.address();
    
public HeaderparseHeader(java.lang.String header)
Parses an individual SIP message header from a string.

param
header String containing the SIP header.
return
a Header structure.
exception
ParseException if there was an error parsing the message.

        // It's not clear why "\n\n" was added to the header.
        // header += "\n\n";

        // Handle line folding.
        String nmessage = StringTokenizer.convertNewLines(header);
        nmessage += "\n"; /* why not Separators.NEWLINE ? */

        // System.out.println(">>> '" + nmessage + "'");

        HeaderParser hp = ParserFactory.createParser(nmessage);
        if (hp == null)
            throw new ParseException("could not create parser", 0);

        return hp.parse();
    
public HostparseHost(java.lang.String host)
Parse a host name and return a parsed structure.

param
host is a String containing the host name to be parsed
return
a parsed address structure.
since
v1.0
exception
ParseException when the hostname is badly formatted.

        Lexer lexer = new Lexer("charLexer", host);
        HostNameParser hp = new HostNameParser(lexer);
        return new Host(hp.hostName());

    
public HostPortparseHostPort(java.lang.String hostport)
Parses a host:port and return a parsed structure.

param
hostport is a String containing the host:port to be parsed
return
a parsed address structure.
since
v1.0
exception
ParseException when the address is badly formatted.

        Lexer lexer = new Lexer("charLexer", hostport);
        return new HostNameParser(lexer).hostPort();

    
private MessageparseMessage(java.lang.String currentMessage)
This is called repeatedly by parseMessage to parse the contents of a message buffer. This assumes the message already has continuations etc. taken care of. prior to its being called.

param
currentMessage current message to process
return
parsed message data

        // position line counter at the end of the
        // sip messages.
        //
// System.out.println("parsing <<" + currentMessage+">>");

        Message sipmsg = null;
        StringTokenizer tokenizer = new StringTokenizer(currentMessage, '\n");
        messageHeaders = new Vector(); // A list of headers for error reporting

        while (tokenizer.hasMoreChars()) {
            String nexttok = tokenizer.nextToken();
            if (nexttok.equals("\n")) {
                String nextnexttok = tokenizer.nextToken();
                if (nextnexttok.equals("\n")) {
                    break;
                } else messageHeaders.addElement(nextnexttok);
            } else messageHeaders.addElement(nexttok);
        }

        currentLine = 0;
        currentHeader = (String) messageHeaders.elementAt(currentLine);
        String firstLine = currentHeader;
        // System.out.println("first Line " + firstLine);

        if (!firstLine.startsWith(SIPConstants.SIP_VERSION_STRING)) {
            sipmsg = new Request();
            try {
                RequestLine rl =
                        new RequestLineParser(firstLine+ "\n").parse();
                ((Request) sipmsg).setRequestLine(rl);
            } catch (ParseException ex) {
                if (this.parseExceptionListener != null)
                    this.parseExceptionListener.handleException
                            (ex, sipmsg, new RequestLine().getClass(),
                            firstLine, currentMessage);
                else throw ex;

            }
        } else {
            sipmsg = new Response();

            try {
                StatusLine sl = new StatusLineParser(firstLine + "\n").parse();
                ((Response) sipmsg).setStatusLine(sl);
            } catch (ParseException ex) {
                if (this.parseExceptionListener != null) {
                    this.parseExceptionListener.handleException
                            (ex, sipmsg,
                            new StatusLine().getClass(),
                            firstLine, currentMessage);
                } else throw ex;

            }
        }

        for (int i = 1; i < messageHeaders.size(); i++) {
            String hdrstring = (String) messageHeaders.elementAt(i);

            if (hdrstring == null || hdrstring.trim().equals("")) {
                continue;
            }

            HeaderParser hdrParser = null;

            try {
                // System.out.println("'" + hdrstring + "'");
                hdrParser = ParserFactory.createParser(hdrstring + "\n");
            } catch (ParseException ex) {
                parseExceptionListener.handleException(
                    ex, sipmsg, null, hdrstring, currentMessage);
                continue;
            }

            Header sipHeader = null;

            try {
                sipHeader = hdrParser.parse();
                sipmsg.attachHeader(sipHeader, false);
            } catch (ParseException ex) {
                // ex.printStackTrace();
                if (parseExceptionListener != null) {
                    String hdrName = Lexer.getHeaderName(hdrstring);
                    Class hdrClass = NameMap.getClassFromName(hdrName);

                    if (hdrClass == null) {
                        hdrClass = ExtensionHeader.clazz;
                    }

                    parseExceptionListener.handleException(
                        ex, sipmsg, hdrClass, hdrstring, currentMessage);
                } else { // use generic parser
                    hdrParser  = new ExtensionParser(hdrstring + "\n");
                    sipHeader = hdrParser.parse();
                    try {
                        sipmsg.attachHeader(sipHeader, false);
                    } catch (SipException exc) {
                        throw new ParseException(sipHeader.toString(), 0);
                    }
                }
            } catch (SipException ex) {
                // Invalid header.
                throw new ParseException(sipHeader.toString(), 0);
            }
        }

        return sipmsg;
    
public RequestLineparseRequestLine(java.lang.String requestLine)
Parses the SIP Request Line

param
requestLine a String containing the request line to be parsed.
return
a RequestLine structure that has the parsed RequestLine
exception
ParseException if there was an error parsing the requestLine.

        requestLine += "\n";
        return new RequestLineParser(requestLine).parse();
    
public MessageparseSIPMessage(java.lang.String sipMessages)
Parses a buffer containing one or more SIP Messages and return an array of Message parsed structures. Note that the current limitation is that this does not handle content encoding properly. The message content is just assumed to be encoded using the same encoding as the sip message itself (i.e. binary encodings such as gzip are not supported).

param
sipMessages a String containing the messages to be parsed. This can consist of multiple SIP Messages concatenated together.
return
a Message 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

        // Handle line folding and evil DOS CR-LF sequences
        rawMessage = sipMessages;
        Vector retval = new Vector();
        String pmessage = sipMessages;
        bodyIsString = true;

        this.contentLength = 0;
        if (pmessage.trim().equals(""))
            return null;

        pmessage += "\n\n";
        StringBuffer message = new StringBuffer(pmessage);
        // squeeze out the leading crlf sequences.
        while (message.charAt(0) == '\r" || message.charAt(0) == '\n") {
            bufferPointer ++;
            message.deleteCharAt(0);
        }

        // squeeze out the crlf sequences and make them uniformly CR
        String message1 = message.toString();
        int length;
        length = message1.indexOf("\r\n\r\n");
        if (length > 0) length += 4;
        if (length == -1) {
            length = message1.indexOf("\n\n");
            if (length == -1)
                throw new ParseException("no trailing crlf", 0);
        } else length += 2;


        // Get rid of CR to make it uniform.
        for (int k = 0; k < length; k++) {
            if (message.charAt(k) == '\r") {
                message.deleteCharAt(k);
                length --;
            }
        }


        if (debugFlag) {
            for (int k = 0; k < length; k++) {
                rawMessage1 = rawMessage1 + "[" + message.charAt(k) +"]";
            }
        }

        // The following can be written more efficiently in a single pass
        // but it is somewhat complex.
        StringTokenizer tokenizer = new StringTokenizer
                (message.toString(), '\n");
        StringBuffer cooked_message = new StringBuffer();
        while (tokenizer.hasMoreChars()) {
            String nexttok = tokenizer.nextToken();
            // Ignore blank lines with leading spaces or tabs.
            if (nexttok.trim().equals("")) cooked_message.append("\n");
            else cooked_message.append(nexttok);
        }

        cooked_message = normalizeMessage(cooked_message);
        cooked_message.append("\n\n");


        // Separate the string out into substrings for
        // error reporting.

        currentMessage = cooked_message.toString();

        if (Parser.debug) {
            Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                currentMessage);
        }

        bufferPointer = currentMessage.indexOf("\n\n") + 3;
        Message sipmsg = this.parseMessage(currentMessage);
        if (readBody && sipmsg.getContentLengthHeader() != null &&
                sipmsg.getContentLengthHeader().getContentLength() != 0) {
            this.contentLength =
                    sipmsg.getContentLengthHeader().getContentLength();
            String body = this.getMessageBody();
            sipmsg.setMessageContent(body);
        }
        return sipmsg;


    
public MessageparseSIPMessage(byte[] msgBuffer)
Parses 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 Message[] 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


        bufferPointer = 0;
        bodyIsString = false;
        currentMessageBytes = msgBuffer;
        int start;
        // Squeeze out leading CRLF
        // Squeeze out the leading nulls (otherwise the parser will crash)
        for (start = bufferPointer; start < msgBuffer.length; start++) {
            final char chr = (char)msgBuffer[start];
            if (chr != '\r"
             && chr != '\n"
             && chr != '\0") break;
        }


        if (start == msgBuffer.length)
            return null;

        // Find the end of the SIP message.
        int fin;
        for (fin = start; fin < msgBuffer.length -4; fin ++) {
            if ((char) msgBuffer[fin] == '\r"
             && (char) msgBuffer[fin+1] == '\n"
             && (char) msgBuffer[fin+2] == '\r"
             && (char) msgBuffer[fin+3] == '\n") {
                break;
            }
        }
        if (fin < msgBuffer.length) {
            // we do not handle the (theoretically possible) case that the
            // headers end with LFLF but there *is* CRLFCRLF in the body
            fin += 4;
        } else {
            // Could not find CRLFCRLF end of message so look for LFLF
            for (fin = start; fin < msgBuffer.length -2; fin++) {
                if ((char)msgBuffer[fin] == '\n"
                 && (char)msgBuffer[fin+1] == '\n") break;
            }
            if (fin < msgBuffer.length) fin += 2;
            else throw new ParseException("Message not terminated", 0);
        }

        // Encode the body as a UTF-8 string.
        String messageString = null;
        try {
            messageString = new String(msgBuffer, start, fin - start, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            throw new ParseException("Bad message encoding!", 0);
        }
        bufferPointer = fin;
        int length = messageString.length();
        StringBuffer message = new StringBuffer(length);
        // Get rid of CR to make it uniform for the parser.
        for (int k = 0; k < length; k++) {
            final char currChar = messageString.charAt(k);
            if (currChar != '\r") {
                message.append(currChar);
            }
        }
        length = message.length();

        if (Parser.debug) {
            for (int k = 0; k < length; k++) {
                rawMessage1 = rawMessage1 + "[" + message.charAt(k) +"]";
            }
        }

        // The following can be written more efficiently in a single pass
        // but it is somewhat complex.
        StringTokenizer tokenizer = new StringTokenizer
                (message.toString(), '\n");
        StringBuffer cooked_message = new StringBuffer();
        while (tokenizer.hasMoreChars()) {
            String nexttok = tokenizer.nextToken();
            // Ignore blank lines with leading spaces or tabs.
            if (nexttok.trim().equals("")) cooked_message.append("\n");
            else cooked_message.append(nexttok);
        }

        cooked_message = normalizeMessage(cooked_message);
        cooked_message.append("\n\n");

        // Separate the string out into substrings for
        // error reporting.
        currentMessage = cooked_message.toString();
        Message sipmsg = parseMessage(currentMessage);
        if (readBody && sipmsg.getContentLengthHeader() != null
                && sipmsg.getContentLengthHeader().getContentLength() != 0) {
            contentLength = sipmsg.getContentLengthHeader().getContentLength();
            byte body[] = getBodyAsBytes();
            sipmsg.setMessageContent(body);
        }
        // System.out.println("Parsed = [" + sipmsg + "]");
        return sipmsg;

    
public StatusLineparseSIPStatusLine(java.lang.String statusLine)
Parses the SIP Response message status line

param
statusLine a String containing the Status line to be parsed.
return
StatusLine class corresponding to message
exception
ParseException if there was an error parsing
see
StatusLine

        statusLine += "\n";
        return new StatusLineParser(statusLine).parse();
    
public SipURIparseSIPUrl(java.lang.String url)
Parses a SIP url from a string and return a URI structure for it.

param
url a String containing the URI structure to be parsed.
return
A parsed URI structure
exception
ParseException if there was an error parsing the message.

        try {
            URLParser parser = new URLParser(url);
            SipURI uri = (SipURI)parser.parse();

            // whole string has to be consumed
            // otherwise it is wrong URL or not URL only
            if (parser.getLexer().hasMoreChars()) {
                throw new ParseException(url + " Not a URL string",
                                         parser.getLexer().getPtr());
            }
            return  uri;
        } catch (ClassCastException ex) {
            throw new ParseException(url + " Not a SIP URL ", 0);
        }
    
public TelephoneNumberparseTelephoneNumber(java.lang.String telephone_number)
Parses a telephone number return a parsed structure.

param
telephone_number is a String containing the telephone # to be parsed
return
a parsed address structure.
since
v1.0
exception
ParseException when the address is badly formatted.

        return new URLParser(telephone_number).parseTelephoneNumber();

    
public URIparseUrl(java.lang.String url)
Parses a uri from a string and return a URI structure for it.

param
url a String containing the URI structure to be parsed.
return
A parsed URI structure
exception
ParseException if there was an error parsing the message.

        return new URLParser(url).parse();
    
protected byte[]readBytesToEnd()
Returns the bytes to the end of the message. This is invoked when the parser is invoked with an array of bytes rather than with a string.

return
the bytes to the end of message

        byte[] body = new byte[currentMessageBytes.length - bufferPointer];
        int endIndex = currentMessageBytes.length;
        for (int i = bufferPointer, k = 0; i < endIndex; i++, k++) {
            body[k] = currentMessageBytes[i];
        }
        bufferPointer = endIndex;
        this.contentLength = 0;
        return body;
    
protected java.lang.StringreadToEnd()
Returns the contents till the end of the buffer (this is useful when you encounter an error.

return
text up to end of message

        String body = currentMessage.substring(bufferPointer);
        bufferPointer += body.length();
        return body;
    
public voidsetParseExceptionListener(ParseExceptionListener pexhandler)
Adds a handler for header parsing errors.

param
pexhandler is a class that implements the ParseExceptionListener interface.

        parseExceptionListener = pexhandler;