All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nist.javax.sip.parser.chars.CharsMsgParser Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

/*******************************************************************************
 * Product of NIST/ITL Advanced Networking Technologies Division (ANTD)        *
 ******************************************************************************/

package gov.nist.javax.sip.parser.chars;

import gov.nist.core.Host;
import gov.nist.javax.sip.SIPConstants;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.GenericURI;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.address.TelephoneNumber;
import gov.nist.javax.sip.header.ExtensionHeaderImpl;
import gov.nist.javax.sip.header.NameMap;
import gov.nist.javax.sip.header.RequestLine;
import gov.nist.javax.sip.header.SIPHeader;
import gov.nist.javax.sip.header.StatusLine;
import gov.nist.javax.sip.message.SIPMessage;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.parser.MessageParser;
import gov.nist.javax.sip.parser.ParseExceptionListener;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.Arrays;

/**
 * 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 SIPHeader 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
 * parseSIPMessage(byte[]) else use parseSIPMessage(String) The payload is
 * accessible from the parsed message using the getContent and getContentBytes
 * methods provided by the SIPMessage class. If SDP parsing is enabled using the
 * parseContent method, then the SDP body is also parsed and can be accessed
 * from the message using the getSDPAnnounce method. Currently only eager
 * parsing of the message is supported (i.e. the entire message is parsed in one
 * feld swoop).
 *
 *
 * @version 1.2 $Revision: 1.27 $ $Date: 2010/03/15 17:01:21 $
 *
 * @author M. Ranganathan
 *
  *
 */
public class CharsMsgParser implements MessageParser {

//    protected boolean readBody;
//    protected ParseExceptionListener parseExceptionListener;
//    protected String rawStringMessage;
//    protected boolean strict;

    protected static boolean computeContentLengthFromMessage = false;
    protected static final Charset charset = Charset.forName("UTF-8");
    protected static final char[] SIP_VERSION_CHAR = SIPConstants.SIP_VERSION_STRING.toCharArray();
    /**
     * @since v0.9
     */
    public CharsMsgParser() {
        super();
//        readBody = true;
    }

//    /**
//     * Constructor (given a parse exception handler).
//     *
//     * @since 1.0
//     * @param exhandler
//     *            is the parse exception listener for the message parser.
//     */
//    public CharsMsgParser(ParseExceptionListener exhandler) {
//        this();
//        parseExceptionListener = exhandler;
//    }
//
//    /**
//     * Add a handler for header parsing errors.
//     *
//     * @param pexhandler
//     *            is a class that implements the ParseExceptionListener
//     *            interface.
//     */
//    public void setParseExceptionListener(ParseExceptionListener pexhandler) {
//        parseExceptionListener = pexhandler;
//    }

    /**
     * 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;
    }

    protected static char[] trimEndOfLine(char[] line) {
        if (line == null)
            return line;

        int i = line.length - 1;
        while (i >= 0 && line[i] <= 0x20)
            i--;

        if (i == line.length - 1)
            return line;

        if (i == -1)
            return "".intern().toCharArray();

        char[] retval = new char[i+1];
        System.arraycopy(line, 0, retval, 0, i+1);
        return retval;
    }

    protected SIPMessage processFirstLine(char[] firstLine, ParseExceptionListener parseExceptionListener, byte[] msgBuffer) throws ParseException {
        SIPMessage message;
        
        char[] retval = new char[firstLine.length + 1];
        System.arraycopy(firstLine, 0, retval, 0, firstLine.length);
        retval[firstLine.length] = '\n';
        
        char[] sipVersionCompare = new char[7];
        System.arraycopy(firstLine, 0, sipVersionCompare, 0, 7);        
        
        if (!Arrays.equals(sipVersionCompare, SIP_VERSION_CHAR)) {
            message = new SIPRequest();
            try {
                RequestLine requestLine = new RequestLineParser(retval)
                        .parse();
                ((SIPRequest) message).setRequestLine(requestLine);
            } catch (ParseException ex) {
                if (parseExceptionListener != null)
                    parseExceptionListener.handleException(ex, message,
                            RequestLine.class, String.valueOf(firstLine), String.valueOf(msgBuffer));
                else
                    throw ex;

            }
        } else {
            message = new SIPResponse();
            try {
                StatusLine sl = new StatusLineParser(retval).parse();
                ((SIPResponse) message).setStatusLine(sl);
            } catch (ParseException ex) {
                if (parseExceptionListener != null) {
                    parseExceptionListener.handleException(ex, message,
                            StatusLine.class, String.valueOf(firstLine), String.valueOf(msgBuffer));
                } else
                    throw ex;

            }
        }
        return message;
    }

    protected void processHeader(char[] header, SIPMessage message, ParseExceptionListener parseExceptionListener, byte[] msgBuffer) throws ParseException {
        if (header == null || header.length == 0)
            return;

        HeaderParser headerParser = null;
        try {        	        	
            headerParser = ParserFactory.createParser(header);
        } catch (ParseException ex) {
            parseExceptionListener.handleException(ex, message, null,
            		String.valueOf(header), String.valueOf(msgBuffer));
            return;
        }

        try {
            SIPHeader sipHeader = headerParser.parse();
            message.attachHeader(sipHeader, false);
        } catch (ParseException ex) {
            if (parseExceptionListener != null) {
                String headerName = Lexer.getHeaderName(header);
                Class headerClass = NameMap.getClassFromName(headerName);
                if (headerClass == null) {
                    headerClass = ExtensionHeaderImpl.class;

                }
                parseExceptionListener.handleException(ex, message,
                        headerClass, String.valueOf(header), String.valueOf(msgBuffer));

            }
        }
    }

    /**
     * Parse 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.
     */
    public AddressImpl parseAddress(char[] address) throws ParseException {
        AddressParser addressParser = new AddressParser(address);
        return addressParser.address(true);
    }

    /**
     * Parse 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 throws
     *                a ParseException when the address is badly formatted.
     *
    public HostPort parseHostPort(String hostport) throws ParseException {
        Lexer lexer = new Lexer("charLexer", hostport);
        return new HostNameParser(lexer).hostPort();

    }
    */

    /**
     * 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
     *                a ParseException when the hostname is badly formatted.
     */
    public Host parseHost(char[] host) throws ParseException {
        Lexer lexer = new Lexer("charLexer", host);
        return new HostNameParser(lexer).host();

    }

    /**
     * Parse 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
     *                a ParseException when the address is badly formatted.
     */
    public TelephoneNumber parseTelephoneNumber(char[] telephone_number)
            throws ParseException {
        // Bug fix contributed by Will Scullin
        return new URLParser(telephone_number).parseTelephoneNumber(true);

    }

    /**
     * Parse 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.
     */

    public SipUri parseSIPUrl(char[] url) throws ParseException {
        try {
            return new URLParser(url).sipURL(true);
        } catch (ClassCastException ex) {
            throw new ParseException(url + " Not a SIP URL ", 0);
        }
    }

    /**
     * Parse 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.
     */

    public GenericURI parseUrl(char[] url) throws ParseException {
        return new URLParser(url).parse();
    }

    /**
     * Parse an individual SIP message header from a string.
     *
     * @param header
     *            String containing the SIP header.
     * @return a SIPHeader structure.
     * @exception ParseException
     *                if there was an error parsing the message.
     */
//    public static SIPHeader parseSIPHeader(String header) throws ParseException {
//        int start = 0;
//        int end = header.length() - 1;
//        try {
//            // Squeeze out any leading control character.
//            while (header.charAt(start) <= 0x20)
//                start++;
//
//            // Squeeze out any trailing control character.
//            while (header.charAt(end) <= 0x20)
//                end--;
//        }
//        catch (ArrayIndexOutOfBoundsException e) {
//            // Array contains only control char.
//            throw new ParseException("Empty header.", 0);
//        }
//
//        StringBuilder buffer = new StringBuilder(end + 1);
//        int i = start;
//        int lineStart = start;
//        boolean endOfLine = false;
//        while (i <= end) {
//            char c = header.charAt(i);
//            if (c == '\r' || c == '\n') {
//                if (!endOfLine) {
//                    buffer.append(header.substring(lineStart, i));
//                    endOfLine = true;
//                }
//            }
//            else {
//                if (endOfLine) {
//                    endOfLine = false;
//                    if (c == ' ' || c == '\t') {
//                        buffer.append(' ');
//                        lineStart = i + 1;
//                    }
//                    else {
//                        lineStart = i;
//                    }
//                }
//            }
//
//            i++;
//        }
//        buffer.append(header.substring(lineStart, i));
//        buffer.append('\n');
//
//        HeaderParser hp = ParserFactory.createParser(buffer);
//        if (hp == null)
//            throw new ParseException("could not create parser", 0);
//        return hp.parse();
//    }

    /**
     * Parse 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.
     */

    public RequestLine parseSIPRequestLine(char[] requestLine)
            throws ParseException {
//        requestLine += "\n";
        char[] retval = new char[requestLine.length + 1];
        System.arraycopy(requestLine, 0, retval, 0, requestLine.length);
        retval[requestLine.length] = '\n';
        
        return new RequestLineParser(retval).parse();
    }

    /**
     * Parse 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
     */

    public StatusLine parseSIPStatusLine(char[] statusLine)
            throws ParseException {
//        statusLine += "\n";
        char[] retval = new char[statusLine.length + 1];
        System.arraycopy(statusLine, 0, retval, 0, statusLine.length);
        retval[statusLine.length] = '\n';
        
        return new StatusLineParser(statusLine).parse();
    }

    public static void setComputeContentLengthFromMessage(
            boolean computeContentLengthFromMessage) {
        CharsMsgParser.computeContentLengthFromMessage = computeContentLengthFromMessage;
    }



    /**
     * Test code.
     */
    public static void main(String[] args) throws ParseException {
        String messages[] = {
                "SIP/2.0 200 OK\r\n"
                        + "To: \"The Little Blister\" ;tag=469bc066\r\n"
                        + "From: \"The Master Blaster\" ;tag=11\r\n"
                        + "Via: SIP/2.0/UDP 139.10.134.246:5060;branch=z9hG4bK8b0a86f6_1030c7d18e0_17;received=139.10.134.246\r\n"
                        + "Call-ID: 1030c7d18ae_a97b0b_b@8b0a86f6\r\n"
                        + "CSeq: 1 SUBSCRIBE\r\n"
                        + "Contact: \r\n"
                        + "Content-Length: 0\r\n\r\n",

                "SIP/2.0 180 Ringing\r\n"
                        + "Via: SIP/2.0/UDP 172.18.1.29:5060;branch=z9hG4bK43fc10fb4446d55fc5c8f969607991f4\r\n"
                        + "To: \"0440\" ;tag=2600\r\n"
                        + "From: \"Andreas\" ;tag=8524\r\n"
                        + "Call-ID: [email protected]\r\n"
                        + "CSeq: 1 INVITE\r\n" + "Max-Forwards: 70\r\n"
                        + "Record-Route: \r\n"
                        + "Content-Length: 0\r\n\r\n",
                "REGISTER sip:nist.gov SIP/2.0\r\n"
                        + "Via: SIP/2.0/UDP 129.6.55.182:14826\r\n"
                        + "Max-Forwards: 70\r\n"
                        + "From: ;tag=6fcd5c7ace8b4a45acf0f0cd539b168b;epid=0d4c418ddf\r\n"
                        + "To: \r\n"
                        + "Call-ID: [email protected]\r\n"
                        + "CSeq: 1 REGISTER\r\n"
                        + "Contact: ;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER\"\r\n"
                        + "User-Agent: RTC/(Microsoft RTC)\r\n"
                        + "Event:  registration\r\n"
                        + "Allow-Events: presence\r\n"
                        + "Content-Length: 0\r\n\r\n"
                        + "INVITE sip:[email protected]:5060 SIP/2.0\r\n"
                        + "Via: SIP/2.0/UDP 65.243.118.100:5050\r\n"
                        + "From: M. Ranganathan  ;tag=1234\r\n"
                        + "To: \"[email protected]\"  \r\n"
                        + "Call-ID: [email protected] \r\n"
                        + "CSeq: 1 INVITE \r\n"
                        + "Content-Length: 247\r\n\r\n"
                        + "v=0\r\n"
                        + "o=4855 13760799956958020 13760799956958020 IN IP4  129.6.55.78\r\n"
                        + "s=mysession session\r\n" + "p=+46 8 52018010\r\n"
                        + "c=IN IP4  129.6.55.78\r\n" + "t=0 0\r\n"
                        + "m=audio 6022 RTP/AVP 0 4 18\r\n"
                        + "a=rtpmap:0 PCMU/8000\r\n"
                        + "a=rtpmap:4 G723/8000\r\n"
                        + "a=rtpmap:18 G729A/8000\r\n" + "a=ptime:20\r\n" };

        class ParserThread implements Runnable {
            String[] messages;

            public ParserThread(String[] messagesToParse) {
                this.messages = messagesToParse;
            }

            public void run() {
                for (int i = 0; i < messages.length; i++) {
                    CharsMsgParser smp = new CharsMsgParser();
                    try {
                        SIPMessage sipMessage = smp
                                .parseSIPMessage(messages[i].getBytes(), true, true, null);
                        System.out.println(" i = " + i + " branchId = "
                                + sipMessage.getTopmostVia().getBranch());
                        // System.out.println("encoded " +
                        // sipMessage.toString());
                    } catch (ParseException ex) {
                    	ex.printStackTrace();
                    }

                    // System.out.println("dialog id = " +
                    // sipMessage.getDialogId(false));
                }
            }
        }

//        for (int i = 0; i < 20; i++) {
//            new Thread(new ParserThread(messages)).start();
//        }
        new ParserThread(messages).run();

    }

//    public void setStrict(boolean strict) {
//       this.strict = strict;
//        
//    }
//
//	public void setReadBody(boolean readBody) {
//		this.readBody = readBody;
//	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy