001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
003: * Reserved. Use is subject to license terms.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: /*
026: */
027: package gov.nist.siplite.parser;
028:
029: import java.util.Vector;
030: import java.io.*;
031: import javax.microedition.sip.SipException;
032:
033: import gov.nist.siplite.*;
034: import gov.nist.siplite.header.*;
035: import gov.nist.siplite.message.*;
036: import gov.nist.siplite.address.*;
037: import gov.nist.core.*;
038:
039: import com.sun.midp.log.Logging;
040: import com.sun.midp.log.LogChannels;
041:
042: /**
043: * Parse SIP message and parts of SIP messages such as URI's etc
044: * from memory and return a structure.
045: * Intended use: UDP message processing.
046: * This class is used when you have an entire SIP message or Header
047: * or SIP URL in memory and you want to generate a parsed structure from
048: * it. For SIP messages, the payload can be binary or String.
049: * If you have a binary payload,
050: * use parseMessage(byte[]) else use parseSIPMessage(String)
051: * The payload is accessible from the parsed message using the getContent and
052: * getContentBytes methods provided by the Message class.
053: * Currently only eager parsing of the message is supported (i.e. the
054: * entire message is parsed in one feld swoop).
055: *
056: *
057: * @version JAIN-SIP-1.1
058: *
059: *
060: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
061: *
062: */
063: public class StringMsgParser {
064: /** Flag indicating body read requested. */
065: protected boolean readBody;
066: /** Unprocessed message part 1 (for error reporting). */
067: private String rawMessage;
068: /** Unprocessed message part 2 (for error reporting). */
069: private String rawMessage1;
070: /** Current message. */
071: private String currentMessage;
072: /** Parsing exeception listener. */
073: private ParseExceptionListener parseExceptionListener;
074: /** Message headers. */
075: private Vector messageHeaders;
076: /** Current buffer pointer. */
077: private int bufferPointer;
078: /** Flag indicating bodyis a text string. */
079: private boolean bodyIsString;
080: /** Current message contents as an arrayof bytes. */
081: private byte[] currentMessageBytes;
082: /** Lengthg of current message body. */
083: protected int contentLength;
084: /** Debugging enabled flag. */
085: private boolean debugFlag;
086: /** Current line being parsed. */
087: private int currentLine;
088: /** Current header being processed. */
089: private String currentHeader;
090:
091: /** Default constructor. */
092: public StringMsgParser() {
093: super ();
094: messageHeaders = new Vector(10, 10);
095: bufferPointer = 0;
096: currentLine = 0;
097: readBody = true;
098: }
099:
100: /**
101: * Constructor (given a parse exception handler).
102: * @since 1.0
103: * @param exhandler is the parse exception listener for the message parser.
104: */
105: public StringMsgParser(ParseExceptionListener exhandler) {
106: this ();
107: parseExceptionListener = exhandler;
108: }
109:
110: /**
111: * Gets the message body.
112: * @return the message body
113: */
114: protected String getMessageBody() {
115:
116: if (this .contentLength == 0) {
117: return null;
118: } else {
119: int endIndex = bufferPointer + this .contentLength;
120: String body;
121: // guard against bad specifications.
122: if (endIndex > currentMessage.length()) {
123: endIndex = currentMessage.length();
124: body = currentMessage
125: .substring(bufferPointer, endIndex);
126: bufferPointer = endIndex;
127: } else {
128: body = currentMessage
129: .substring(bufferPointer, endIndex);
130: bufferPointer = endIndex + 1;
131: }
132: this .contentLength = 0;
133: return body;
134: }
135:
136: }
137:
138: /**
139: * Gets the message body as a byte array.
140: * @return the mesage body
141: */
142: protected byte[] getBodyAsBytes() {
143: if (this .contentLength == 0) {
144: return null;
145: } else {
146: int endIndex = bufferPointer + this .contentLength;
147: // guard against bad specifications.
148: if (endIndex > currentMessageBytes.length) {
149: endIndex = currentMessageBytes.length;
150: }
151: byte[] body = new byte[endIndex - bufferPointer];
152: System.arraycopy(currentMessageBytes, bufferPointer, body,
153: 0, body.length);
154: bufferPointer = endIndex;
155: this .contentLength = 0;
156: return body;
157: }
158:
159: }
160:
161: /**
162: * Returns the contents till the end of the buffer (this is useful when
163: * you encounter an error.
164: * @return text up to end of message
165: */
166: protected String readToEnd() {
167: String body = currentMessage.substring(bufferPointer);
168: bufferPointer += body.length();
169: return body;
170: }
171:
172: /**
173: * Returns the bytes to the end of the message.
174: * This is invoked when the parser is invoked with an array of bytes
175: * rather than with a string.
176: * @return the bytes to the end of message
177: */
178: protected byte[] readBytesToEnd() {
179: byte[] body = new byte[currentMessageBytes.length
180: - bufferPointer];
181: int endIndex = currentMessageBytes.length;
182: for (int i = bufferPointer, k = 0; i < endIndex; i++, k++) {
183: body[k] = currentMessageBytes[i];
184: }
185: bufferPointer = endIndex;
186: this .contentLength = 0;
187: return body;
188: }
189:
190: /**
191: * Adds a handler for header parsing errors.
192: * @param pexhandler is a class
193: * that implements the ParseExceptionListener interface.
194: */
195: public void setParseExceptionListener(
196: ParseExceptionListener pexhandler) {
197: parseExceptionListener = pexhandler;
198: }
199:
200: /**
201: * Returns true if the body is encoded as a string.
202: * If the parseMessage(String) method is invoked then the body
203: * is assumed to be a string.
204: * @return true if body is a string
205: */
206: protected boolean isBodyString() {
207: return bodyIsString;
208: }
209:
210: /**
211: * Parses a buffer containing a single SIP Message where the body
212: * is an array of un-interpreted bytes. This is intended for parsing
213: * the message from a memory buffer when the buffer.
214: * Incorporates a bug fix for a bug that was noted by Will Sullin of
215: * Callcast
216: * @param msgBuffer a byte buffer containing the messages to be parsed.
217: * This can consist of multiple SIP Messages concatenated together.
218: * @return a Message[] structure (request or response)
219: * containing the parsed SIP message.
220: * @exception ParseException is thrown when an
221: * illegal message has been encountered (and
222: * the rest of the buffer is discarded).
223: * @see ParseExceptionListener
224: */
225: public Message parseSIPMessage(byte[] msgBuffer)
226: throws ParseException {
227:
228: bufferPointer = 0;
229: bodyIsString = false;
230: currentMessageBytes = msgBuffer;
231: int start;
232: // Squeeze out leading CRLF
233: // Squeeze out the leading nulls (otherwise the parser will crash)
234: for (start = bufferPointer; start < msgBuffer.length; start++) {
235: final char chr = (char) msgBuffer[start];
236: if (chr != '\r' && chr != '\n' && chr != '\0')
237: break;
238: }
239:
240: if (start == msgBuffer.length)
241: return null;
242:
243: // Find the end of the SIP message.
244: int fin;
245: for (fin = start; fin < msgBuffer.length - 4; fin++) {
246: if ((char) msgBuffer[fin] == '\r'
247: && (char) msgBuffer[fin + 1] == '\n'
248: && (char) msgBuffer[fin + 2] == '\r'
249: && (char) msgBuffer[fin + 3] == '\n') {
250: break;
251: }
252: }
253: if (fin < msgBuffer.length) {
254: // we do not handle the (theoretically possible) case that the
255: // headers end with LFLF but there *is* CRLFCRLF in the body
256: fin += 4;
257: } else {
258: // Could not find CRLFCRLF end of message so look for LFLF
259: for (fin = start; fin < msgBuffer.length - 2; fin++) {
260: if ((char) msgBuffer[fin] == '\n'
261: && (char) msgBuffer[fin + 1] == '\n')
262: break;
263: }
264: if (fin < msgBuffer.length)
265: fin += 2;
266: else
267: throw new ParseException("Message not terminated", 0);
268: }
269:
270: // Encode the body as a UTF-8 string.
271: String messageString = null;
272: try {
273: messageString = new String(msgBuffer, start, fin - start,
274: "UTF-8");
275: } catch (UnsupportedEncodingException ex) {
276: throw new ParseException("Bad message encoding!", 0);
277: }
278: bufferPointer = fin;
279: int length = messageString.length();
280: StringBuffer message = new StringBuffer(length);
281: // Get rid of CR to make it uniform for the parser.
282: for (int k = 0; k < length; k++) {
283: final char currChar = messageString.charAt(k);
284: if (currChar != '\r') {
285: message.append(currChar);
286: }
287: }
288: length = message.length();
289:
290: if (Parser.debug) {
291: for (int k = 0; k < length; k++) {
292: rawMessage1 = rawMessage1 + "[" + message.charAt(k)
293: + "]";
294: }
295: }
296:
297: // The following can be written more efficiently in a single pass
298: // but it is somewhat complex.
299: StringTokenizer tokenizer = new StringTokenizer(message
300: .toString(), '\n');
301: StringBuffer cooked_message = new StringBuffer();
302: while (tokenizer.hasMoreChars()) {
303: String nexttok = tokenizer.nextToken();
304: // Ignore blank lines with leading spaces or tabs.
305: if (nexttok.trim().equals(""))
306: cooked_message.append("\n");
307: else
308: cooked_message.append(nexttok);
309: }
310:
311: cooked_message = normalizeMessage(cooked_message);
312: cooked_message.append("\n\n");
313:
314: // Separate the string out into substrings for
315: // error reporting.
316: currentMessage = cooked_message.toString();
317: Message sipmsg = parseMessage(currentMessage);
318: if (readBody
319: && sipmsg.getContentLengthHeader() != null
320: && sipmsg.getContentLengthHeader().getContentLength() != 0) {
321: contentLength = sipmsg.getContentLengthHeader()
322: .getContentLength();
323: byte body[] = getBodyAsBytes();
324: sipmsg.setMessageContent(body);
325: }
326: // System.out.println("Parsed = [" + sipmsg + "]");
327: return sipmsg;
328:
329: }
330:
331: /**
332: * Parses a buffer containing one or more SIP Messages and return
333: * an array of
334: * Message parsed structures. Note that the current limitation is that
335: * this does not handle content encoding properly. The message content is
336: * just assumed to be encoded using the same encoding as the sip message
337: * itself (i.e. binary encodings such as gzip are not supported).
338: * @param sipMessages a String containing the messages to be parsed.
339: * This can consist of multiple SIP Messages concatenated together.
340: * @return a Message structure (request or response)
341: * containing the parsed SIP message.
342: * @exception ParseException is thrown when an
343: * illegal message has been encountered (and
344: * the rest of the buffer is discarded).
345: * @see ParseExceptionListener
346: */
347: public Message parseSIPMessage(String sipMessages)
348: throws ParseException {
349: // Handle line folding and evil DOS CR-LF sequences
350: rawMessage = sipMessages;
351: Vector retval = new Vector();
352: String pmessage = sipMessages;
353: bodyIsString = true;
354:
355: this .contentLength = 0;
356: if (pmessage.trim().equals(""))
357: return null;
358:
359: pmessage += "\n\n";
360: StringBuffer message = new StringBuffer(pmessage);
361: // squeeze out the leading crlf sequences.
362: while (message.charAt(0) == '\r' || message.charAt(0) == '\n') {
363: bufferPointer++;
364: message.deleteCharAt(0);
365: }
366:
367: // squeeze out the crlf sequences and make them uniformly CR
368: String message1 = message.toString();
369: int length;
370: length = message1.indexOf("\r\n\r\n");
371: if (length > 0)
372: length += 4;
373: if (length == -1) {
374: length = message1.indexOf("\n\n");
375: if (length == -1)
376: throw new ParseException("no trailing crlf", 0);
377: } else
378: length += 2;
379:
380: // Get rid of CR to make it uniform.
381: for (int k = 0; k < length; k++) {
382: if (message.charAt(k) == '\r') {
383: message.deleteCharAt(k);
384: length--;
385: }
386: }
387:
388: if (debugFlag) {
389: for (int k = 0; k < length; k++) {
390: rawMessage1 = rawMessage1 + "[" + message.charAt(k)
391: + "]";
392: }
393: }
394:
395: // The following can be written more efficiently in a single pass
396: // but it is somewhat complex.
397: StringTokenizer tokenizer = new StringTokenizer(message
398: .toString(), '\n');
399: StringBuffer cooked_message = new StringBuffer();
400: while (tokenizer.hasMoreChars()) {
401: String nexttok = tokenizer.nextToken();
402: // Ignore blank lines with leading spaces or tabs.
403: if (nexttok.trim().equals(""))
404: cooked_message.append("\n");
405: else
406: cooked_message.append(nexttok);
407: }
408:
409: cooked_message = normalizeMessage(cooked_message);
410: cooked_message.append("\n\n");
411:
412: // Separate the string out into substrings for
413: // error reporting.
414:
415: currentMessage = cooked_message.toString();
416:
417: if (Parser.debug) {
418: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
419: currentMessage);
420: }
421:
422: bufferPointer = currentMessage.indexOf("\n\n") + 3;
423: Message sipmsg = this .parseMessage(currentMessage);
424: if (readBody
425: && sipmsg.getContentLengthHeader() != null
426: && sipmsg.getContentLengthHeader().getContentLength() != 0) {
427: this .contentLength = sipmsg.getContentLengthHeader()
428: .getContentLength();
429: String body = this .getMessageBody();
430: sipmsg.setMessageContent(body);
431: }
432: return sipmsg;
433:
434: }
435:
436: /**
437: * Normalize message string, i.e. remove whitespace
438: * @param srcMsg message to be processed
439: * @return normalized message
440: */
441: private StringBuffer normalizeMessage(StringBuffer srcMsg) {
442: StringBuffer normalizedMessage = new StringBuffer(srcMsg
443: .length());
444: String message1 = srcMsg.toString();
445: int length = message1.indexOf("\n\n") + 2;
446: int k = 0;
447: while (k < length - 1) {
448: final char this Char = srcMsg.charAt(k);
449: final char thatChar = srcMsg.charAt(k + 1);
450:
451: // is it a continuation line?
452: if (this Char == '\n'
453: && (thatChar == '\t' || thatChar == ' ')) {
454: normalizedMessage.append(' ');
455: k++; // skipping \n
456: // now remove whitespace
457: char nextChar;
458: do {
459: k++; // skipping \t or space
460: if (k == length) {
461: break;
462: }
463: nextChar = srcMsg.charAt(k);
464: } while (nextChar == ' ' || nextChar == '\t');
465: } else {
466: normalizedMessage.append(this Char);
467: k++;
468: }
469: }
470: return normalizedMessage;
471: }
472:
473: /**
474: * This is called repeatedly by parseMessage to parse
475: * the contents of a message buffer. This assumes the message
476: * already has continuations etc. taken care of.
477: * prior to its being called.
478: * @param currentMessage current message to process
479: * @return parsed message data
480: */
481: private Message parseMessage(String currentMessage)
482: throws ParseException {
483: // position line counter at the end of the
484: // sip messages.
485: //
486: // System.out.println("parsing <<" + currentMessage+">>");
487:
488: Message sipmsg = null;
489: StringTokenizer tokenizer = new StringTokenizer(currentMessage,
490: '\n');
491: messageHeaders = new Vector(); // A list of headers for error reporting
492:
493: while (tokenizer.hasMoreChars()) {
494: String nexttok = tokenizer.nextToken();
495: if (nexttok.equals("\n")) {
496: String nextnexttok = tokenizer.nextToken();
497: if (nextnexttok.equals("\n")) {
498: break;
499: } else
500: messageHeaders.addElement(nextnexttok);
501: } else
502: messageHeaders.addElement(nexttok);
503: }
504:
505: currentLine = 0;
506: currentHeader = (String) messageHeaders.elementAt(currentLine);
507: String firstLine = currentHeader;
508: // System.out.println("first Line " + firstLine);
509:
510: if (!firstLine.startsWith(SIPConstants.SIP_VERSION_STRING)) {
511: sipmsg = new Request();
512: try {
513: RequestLine rl = new RequestLineParser(firstLine + "\n")
514: .parse();
515: ((Request) sipmsg).setRequestLine(rl);
516: } catch (ParseException ex) {
517: if (this .parseExceptionListener != null)
518: this .parseExceptionListener.handleException(ex,
519: sipmsg, new RequestLine().getClass(),
520: firstLine, currentMessage);
521: else
522: throw ex;
523:
524: }
525: } else {
526: sipmsg = new Response();
527:
528: try {
529: StatusLine sl = new StatusLineParser(firstLine + "\n")
530: .parse();
531: ((Response) sipmsg).setStatusLine(sl);
532: } catch (ParseException ex) {
533: if (this .parseExceptionListener != null) {
534: this .parseExceptionListener.handleException(ex,
535: sipmsg, new StatusLine().getClass(),
536: firstLine, currentMessage);
537: } else
538: throw ex;
539:
540: }
541: }
542:
543: for (int i = 1; i < messageHeaders.size(); i++) {
544: String hdrstring = (String) messageHeaders.elementAt(i);
545:
546: if (hdrstring == null || hdrstring.trim().equals("")) {
547: continue;
548: }
549:
550: HeaderParser hdrParser = null;
551:
552: try {
553: // System.out.println("'" + hdrstring + "'");
554: hdrParser = ParserFactory
555: .createParser(hdrstring + "\n");
556: } catch (ParseException ex) {
557: parseExceptionListener.handleException(ex, sipmsg,
558: null, hdrstring, currentMessage);
559: continue;
560: }
561:
562: Header sipHeader = null;
563:
564: try {
565: sipHeader = hdrParser.parse();
566: sipmsg.attachHeader(sipHeader, false);
567: } catch (ParseException ex) {
568: // ex.printStackTrace();
569: if (parseExceptionListener != null) {
570: String hdrName = Lexer.getHeaderName(hdrstring);
571: Class hdrClass = NameMap.getClassFromName(hdrName);
572:
573: if (hdrClass == null) {
574: hdrClass = ExtensionHeader.clazz;
575: }
576:
577: parseExceptionListener.handleException(ex, sipmsg,
578: hdrClass, hdrstring, currentMessage);
579: } else { // use generic parser
580: hdrParser = new ExtensionParser(hdrstring + "\n");
581: sipHeader = hdrParser.parse();
582: try {
583: sipmsg.attachHeader(sipHeader, false);
584: } catch (SipException exc) {
585: throw new ParseException(sipHeader.toString(),
586: 0);
587: }
588: }
589: } catch (SipException ex) {
590: // Invalid header.
591: throw new ParseException(sipHeader.toString(), 0);
592: }
593: }
594:
595: return sipmsg;
596: }
597:
598: /**
599: * Parses an address (nameaddr or address spec) and return and address
600: * structure.
601: * @param address is a String containing the address to be parsed.
602: * @return a parsed address structure.
603: * @since v1.0
604: * @exception ParseException when the address is badly formatted.
605: */
606:
607: public Address parseAddress(String address) throws ParseException {
608: AddressParser addressParser = new AddressParser(address);
609: return addressParser.address();
610: }
611:
612: /**
613: * Parses a host:port and return a parsed structure.
614: * @param hostport is a String containing the host:port to be parsed
615: * @return a parsed address structure.
616: * @since v1.0
617: * @exception ParseException when the address is badly formatted.
618: */
619: public HostPort parseHostPort(String hostport)
620: throws ParseException {
621: Lexer lexer = new Lexer("charLexer", hostport);
622: return new HostNameParser(lexer).hostPort();
623:
624: }
625:
626: /**
627: * Parse a host name and return a parsed structure.
628: * @param host is a String containing the host name to be parsed
629: * @return a parsed address structure.
630: * @since v1.0
631: * @exception ParseException when the hostname is badly formatted.
632: */
633: public Host parseHost(String host) throws ParseException {
634: Lexer lexer = new Lexer("charLexer", host);
635: HostNameParser hp = new HostNameParser(lexer);
636: return new Host(hp.hostName());
637:
638: }
639:
640: /**
641: * Parses a telephone number return a parsed structure.
642: * @param telephone_number is a String containing the
643: * telephone # to be parsed
644: * @return a parsed address structure.
645: * @since v1.0
646: * @exception ParseException when the address is badly formatted.
647: */
648: public TelephoneNumber parseTelephoneNumber(String telephone_number)
649: throws ParseException {
650: return new URLParser(telephone_number).parseTelephoneNumber();
651:
652: }
653:
654: /**
655: * Parses a SIP url from a string and return a URI structure for it.
656: * @param url a String containing the URI structure to be parsed.
657: * @return A parsed URI structure
658: * @exception ParseException if there was an error parsing the message.
659: */
660:
661: public SipURI parseSIPUrl(String url) throws ParseException {
662: try {
663: URLParser parser = new URLParser(url);
664: SipURI uri = (SipURI) parser.parse();
665:
666: // whole string has to be consumed
667: // otherwise it is wrong URL or not URL only
668: if (parser.getLexer().hasMoreChars()) {
669: throw new ParseException(url + " Not a URL string",
670: parser.getLexer().getPtr());
671: }
672: return uri;
673: } catch (ClassCastException ex) {
674: throw new ParseException(url + " Not a SIP URL ", 0);
675: }
676: }
677:
678: /**
679: * Parses a uri from a string and return a URI structure for it.
680: * @param url a String containing the URI structure to be parsed.
681: * @return A parsed URI structure
682: * @exception ParseException if there was an error parsing the message.
683: */
684:
685: public URI parseUrl(String url) throws ParseException {
686: return new URLParser(url).parse();
687: }
688:
689: /**
690: * Parses an individual SIP message header from a string.
691: * @param header String containing the SIP header.
692: * @return a Header structure.
693: * @exception ParseException if there was an error parsing the message.
694: */
695: public Header parseHeader(String header) throws ParseException {
696: // It's not clear why "\n\n" was added to the header.
697: // header += "\n\n";
698:
699: // Handle line folding.
700: String nmessage = StringTokenizer.convertNewLines(header);
701: nmessage += "\n"; /* why not Separators.NEWLINE ? */
702:
703: // System.out.println(">>> '" + nmessage + "'");
704:
705: HeaderParser hp = ParserFactory.createParser(nmessage);
706: if (hp == null)
707: throw new ParseException("could not create parser", 0);
708:
709: return hp.parse();
710: }
711:
712: /**
713: * Parses the SIP Request Line
714: * @param requestLine a String containing the request line to be parsed.
715: * @return a RequestLine structure that has the parsed RequestLine
716: * @exception ParseException if there was an error parsing the requestLine.
717: */
718: public RequestLine parseRequestLine(String requestLine)
719: throws ParseException {
720: requestLine += "\n";
721: return new RequestLineParser(requestLine).parse();
722: }
723:
724: /**
725: * Parses the SIP Response message status line
726: * @param statusLine a String containing the Status line to be parsed.
727: * @return StatusLine class corresponding to message
728: * @exception ParseException if there was an error parsing
729: * @see StatusLine
730: */
731: public StatusLine parseSIPStatusLine(String statusLine)
732: throws ParseException {
733: statusLine += "\n";
734: return new StatusLineParser(statusLine).parse();
735: }
736:
737: /**
738: * Gets the current header.
739: * @return the current header
740: */
741: public String getCurrentHeader() {
742: return currentHeader;
743: }
744:
745: /**
746: * Gets the current line number.
747: * @return the current line number
748: */
749: public int getCurrentLineNumber() {
750: return currentLine;
751: }
752: }
|