PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/servers/jain-sip-ext/src/main/java/gov/nist/javax/sip/parser/chars/CharsMsgParser.java

http://mobicents.googlecode.com/
Java | 630 lines | 291 code | 73 blank | 266 comment | 70 complexity | 925538d80ea7bf5bdd7c558fa29ec993 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. /*******************************************************************************
  23. * Product of NIST/ITL Advanced Networking Technologies Division (ANTD) *
  24. ******************************************************************************/
  25. package gov.nist.javax.sip.parser.chars;
  26. import gov.nist.core.Host;
  27. import gov.nist.javax.sip.SIPConstants;
  28. import gov.nist.javax.sip.address.AddressImpl;
  29. import gov.nist.javax.sip.address.GenericURI;
  30. import gov.nist.javax.sip.address.SipUri;
  31. import gov.nist.javax.sip.address.TelephoneNumber;
  32. import gov.nist.javax.sip.header.ExtensionHeaderImpl;
  33. import gov.nist.javax.sip.header.NameMap;
  34. import gov.nist.javax.sip.header.RequestLine;
  35. import gov.nist.javax.sip.header.SIPHeader;
  36. import gov.nist.javax.sip.header.StatusLine;
  37. import gov.nist.javax.sip.message.SIPMessage;
  38. import gov.nist.javax.sip.message.SIPRequest;
  39. import gov.nist.javax.sip.message.SIPResponse;
  40. import gov.nist.javax.sip.parser.MessageParser;
  41. import gov.nist.javax.sip.parser.ParseExceptionListener;
  42. import java.nio.ByteBuffer;
  43. import java.nio.charset.Charset;
  44. import java.text.ParseException;
  45. import java.util.Arrays;
  46. /**
  47. * Parse SIP message and parts of SIP messages such as URI's etc from memory and
  48. * return a structure. Intended use: UDP message processing. This class is used
  49. * when you have an entire SIP message or SIPHeader or SIP URL in memory and you
  50. * want to generate a parsed structure from it. For SIP messages, the payload
  51. * can be binary or String. If you have a binary payload, use
  52. * parseSIPMessage(byte[]) else use parseSIPMessage(String) The payload is
  53. * accessible from the parsed message using the getContent and getContentBytes
  54. * methods provided by the SIPMessage class. If SDP parsing is enabled using the
  55. * parseContent method, then the SDP body is also parsed and can be accessed
  56. * from the message using the getSDPAnnounce method. Currently only eager
  57. * parsing of the message is supported (i.e. the entire message is parsed in one
  58. * feld swoop).
  59. *
  60. *
  61. * @version 1.2 $Revision: 1.27 $ $Date: 2010/03/15 17:01:21 $
  62. *
  63. * @author M. Ranganathan <br/>
  64. *
  65. *
  66. */
  67. public class CharsMsgParser implements MessageParser {
  68. // protected boolean readBody;
  69. // protected ParseExceptionListener parseExceptionListener;
  70. // protected String rawStringMessage;
  71. // protected boolean strict;
  72. protected static boolean computeContentLengthFromMessage = false;
  73. protected static final Charset charset = Charset.forName("UTF-8");
  74. protected static final char[] SIP_VERSION_CHAR = SIPConstants.SIP_VERSION_STRING.toCharArray();
  75. /**
  76. * @since v0.9
  77. */
  78. public CharsMsgParser() {
  79. super();
  80. // readBody = true;
  81. }
  82. // /**
  83. // * Constructor (given a parse exception handler).
  84. // *
  85. // * @since 1.0
  86. // * @param exhandler
  87. // * is the parse exception listener for the message parser.
  88. // */
  89. // public CharsMsgParser(ParseExceptionListener exhandler) {
  90. // this();
  91. // parseExceptionListener = exhandler;
  92. // }
  93. //
  94. // /**
  95. // * Add a handler for header parsing errors.
  96. // *
  97. // * @param pexhandler
  98. // * is a class that implements the ParseExceptionListener
  99. // * interface.
  100. // */
  101. // public void setParseExceptionListener(ParseExceptionListener pexhandler) {
  102. // parseExceptionListener = pexhandler;
  103. // }
  104. /**
  105. * Parse a buffer containing a single SIP Message where the body is an array
  106. * of un-interpreted bytes. This is intended for parsing the message from a
  107. * memory buffer when the buffer. Incorporates a bug fix for a bug that was
  108. * noted by Will Sullin of Callcast
  109. *
  110. * @param msgBuffer
  111. * a byte buffer containing the messages to be parsed. This can
  112. * consist of multiple SIP Messages concatenated together.
  113. * @return a SIPMessage[] structure (request or response) containing the
  114. * parsed SIP message.
  115. * @exception ParseException
  116. * is thrown when an illegal message has been encountered
  117. * (and the rest of the buffer is discarded).
  118. * @see ParseExceptionListener
  119. */
  120. public SIPMessage parseSIPMessage(byte[] msgBuffer, boolean readBody, boolean strict, ParseExceptionListener exhandler) throws ParseException {
  121. if (msgBuffer == null || msgBuffer.length == 0)
  122. return null;
  123. int i = 0;
  124. // Squeeze out any leading control character.
  125. try {
  126. while (msgBuffer[i] < 0x20)
  127. i++;
  128. }
  129. catch (ArrayIndexOutOfBoundsException e) {
  130. // Array contains only control char, return null.
  131. return null;
  132. }
  133. // Iterate thru the request/status line and headers.
  134. char[] currentLine = null;
  135. char[] currentHeader = null;
  136. boolean isFirstLine = true;
  137. SIPMessage message = null;
  138. do
  139. {
  140. currentLine = null;
  141. int lineStart = i;
  142. // Find the length of the line.
  143. try {
  144. while (msgBuffer[i] != '\r' && msgBuffer[i] != '\n')
  145. i++;
  146. }
  147. catch (ArrayIndexOutOfBoundsException e) {
  148. // End of the message.
  149. break;
  150. }
  151. int lineLength = i - lineStart;
  152. ByteBuffer bb = ByteBuffer.wrap(msgBuffer, lineStart, lineLength);
  153. currentLine = charset.decode(bb).array();
  154. currentLine = trimEndOfLine(currentLine);
  155. if (currentLine.length == 0) {
  156. // Last header line, process the previous buffered header.
  157. if (currentHeader != null && message != null) {
  158. processHeader(currentHeader, message, exhandler, msgBuffer);
  159. }
  160. }
  161. else {
  162. if (isFirstLine) {
  163. message = processFirstLine(currentLine, exhandler, msgBuffer);
  164. } else {
  165. char firstChar = currentLine[0];
  166. if (firstChar == '\t' || firstChar == ' ') {
  167. if (currentHeader == null)
  168. throw new ParseException("Bad header continuation.", 0);
  169. //This is a continuation, append it to the previous line.
  170. // currentHeader += currentLine.substring(1);
  171. char[] retval = new char[currentHeader.length + currentLine.length - 1];
  172. System.arraycopy(currentHeader, 0, retval, 0, currentHeader.length);
  173. System.arraycopy(currentHeader, currentHeader.length, currentLine, 1, currentLine.length);
  174. }
  175. else {
  176. if (currentHeader != null && message != null) {
  177. processHeader(currentHeader, message, exhandler, msgBuffer);
  178. }
  179. currentHeader = new char[currentLine.length + 1];
  180. System.arraycopy(currentLine, 0, currentHeader, 0, currentLine.length);
  181. currentHeader[currentLine.length] = '\n';
  182. }
  183. }
  184. }
  185. if (msgBuffer[i] == '\r' && msgBuffer.length > i+1 && msgBuffer[i+1] == '\n')
  186. i++;
  187. i++;
  188. isFirstLine = false;
  189. } while (currentLine.length > 0); // End do - while
  190. currentLine = null;
  191. currentHeader = null;
  192. if (message == null) throw new ParseException("Bad message", 0);
  193. message.setSize(i);
  194. // Check for content legth header
  195. if (readBody && message.getContentLength() != null ) {
  196. if ( message.getContentLength().getContentLength() != 0) {
  197. int bodyLength = msgBuffer.length - i;
  198. byte[] body = new byte[bodyLength];
  199. System.arraycopy(msgBuffer, i, body, 0, bodyLength);
  200. message.setMessageContent(body,!strict,computeContentLengthFromMessage,message.getContentLength().getContentLength());
  201. } else if (!computeContentLengthFromMessage && message.getContentLength().getContentLength() == 0 & strict) {
  202. String last4Chars = new String(msgBuffer, msgBuffer.length - 4, 4);
  203. if(!"\r\n\r\n".equals(last4Chars)) {
  204. throw new ParseException("Extraneous characters at the end of the message ",i);
  205. }
  206. }
  207. }
  208. return message;
  209. }
  210. protected static char[] trimEndOfLine(char[] line) {
  211. if (line == null)
  212. return line;
  213. int i = line.length - 1;
  214. while (i >= 0 && line[i] <= 0x20)
  215. i--;
  216. if (i == line.length - 1)
  217. return line;
  218. if (i == -1)
  219. return "".intern().toCharArray();
  220. char[] retval = new char[i+1];
  221. System.arraycopy(line, 0, retval, 0, i+1);
  222. return retval;
  223. }
  224. protected SIPMessage processFirstLine(char[] firstLine, ParseExceptionListener parseExceptionListener, byte[] msgBuffer) throws ParseException {
  225. SIPMessage message;
  226. char[] retval = new char[firstLine.length + 1];
  227. System.arraycopy(firstLine, 0, retval, 0, firstLine.length);
  228. retval[firstLine.length] = '\n';
  229. char[] sipVersionCompare = new char[7];
  230. System.arraycopy(firstLine, 0, sipVersionCompare, 0, 7);
  231. if (!Arrays.equals(sipVersionCompare, SIP_VERSION_CHAR)) {
  232. message = new SIPRequest();
  233. try {
  234. RequestLine requestLine = new RequestLineParser(retval)
  235. .parse();
  236. ((SIPRequest) message).setRequestLine(requestLine);
  237. } catch (ParseException ex) {
  238. if (parseExceptionListener != null)
  239. parseExceptionListener.handleException(ex, message,
  240. RequestLine.class, String.valueOf(firstLine), String.valueOf(msgBuffer));
  241. else
  242. throw ex;
  243. }
  244. } else {
  245. message = new SIPResponse();
  246. try {
  247. StatusLine sl = new StatusLineParser(retval).parse();
  248. ((SIPResponse) message).setStatusLine(sl);
  249. } catch (ParseException ex) {
  250. if (parseExceptionListener != null) {
  251. parseExceptionListener.handleException(ex, message,
  252. StatusLine.class, String.valueOf(firstLine), String.valueOf(msgBuffer));
  253. } else
  254. throw ex;
  255. }
  256. }
  257. return message;
  258. }
  259. protected void processHeader(char[] header, SIPMessage message, ParseExceptionListener parseExceptionListener, byte[] msgBuffer) throws ParseException {
  260. if (header == null || header.length == 0)
  261. return;
  262. HeaderParser headerParser = null;
  263. try {
  264. headerParser = ParserFactory.createParser(header);
  265. } catch (ParseException ex) {
  266. parseExceptionListener.handleException(ex, message, null,
  267. String.valueOf(header), String.valueOf(msgBuffer));
  268. return;
  269. }
  270. try {
  271. SIPHeader sipHeader = headerParser.parse();
  272. message.attachHeader(sipHeader, false);
  273. } catch (ParseException ex) {
  274. if (parseExceptionListener != null) {
  275. String headerName = Lexer.getHeaderName(header);
  276. Class headerClass = NameMap.getClassFromName(headerName);
  277. if (headerClass == null) {
  278. headerClass = ExtensionHeaderImpl.class;
  279. }
  280. parseExceptionListener.handleException(ex, message,
  281. headerClass, String.valueOf(header), String.valueOf(msgBuffer));
  282. }
  283. }
  284. }
  285. /**
  286. * Parse an address (nameaddr or address spec) and return and address
  287. * structure.
  288. *
  289. * @param address
  290. * is a String containing the address to be parsed.
  291. * @return a parsed address structure.
  292. * @since v1.0
  293. * @exception ParseException
  294. * when the address is badly formatted.
  295. */
  296. public AddressImpl parseAddress(char[] address) throws ParseException {
  297. AddressParser addressParser = new AddressParser(address);
  298. return addressParser.address(true);
  299. }
  300. /**
  301. * Parse a host:port and return a parsed structure.
  302. *
  303. * @param hostport
  304. * is a String containing the host:port to be parsed
  305. * @return a parsed address structure.
  306. * @since v1.0
  307. * @exception throws
  308. * a ParseException when the address is badly formatted.
  309. *
  310. public HostPort parseHostPort(String hostport) throws ParseException {
  311. Lexer lexer = new Lexer("charLexer", hostport);
  312. return new HostNameParser(lexer).hostPort();
  313. }
  314. */
  315. /**
  316. * Parse a host name and return a parsed structure.
  317. *
  318. * @param host
  319. * is a String containing the host name to be parsed
  320. * @return a parsed address structure.
  321. * @since v1.0
  322. * @exception ParseException
  323. * a ParseException when the hostname is badly formatted.
  324. */
  325. public Host parseHost(char[] host) throws ParseException {
  326. Lexer lexer = new Lexer("charLexer", host);
  327. return new HostNameParser(lexer).host();
  328. }
  329. /**
  330. * Parse a telephone number return a parsed structure.
  331. *
  332. * @param telephone_number
  333. * is a String containing the telephone # to be parsed
  334. * @return a parsed address structure.
  335. * @since v1.0
  336. * @exception ParseException
  337. * a ParseException when the address is badly formatted.
  338. */
  339. public TelephoneNumber parseTelephoneNumber(char[] telephone_number)
  340. throws ParseException {
  341. // Bug fix contributed by Will Scullin
  342. return new URLParser(telephone_number).parseTelephoneNumber(true);
  343. }
  344. /**
  345. * Parse a SIP url from a string and return a URI structure for it.
  346. *
  347. * @param url
  348. * a String containing the URI structure to be parsed.
  349. * @return A parsed URI structure
  350. * @exception ParseException
  351. * if there was an error parsing the message.
  352. */
  353. public SipUri parseSIPUrl(char[] url) throws ParseException {
  354. try {
  355. return new URLParser(url).sipURL(true);
  356. } catch (ClassCastException ex) {
  357. throw new ParseException(url + " Not a SIP URL ", 0);
  358. }
  359. }
  360. /**
  361. * Parse a uri from a string and return a URI structure for it.
  362. *
  363. * @param url
  364. * a String containing the URI structure to be parsed.
  365. * @return A parsed URI structure
  366. * @exception ParseException
  367. * if there was an error parsing the message.
  368. */
  369. public GenericURI parseUrl(char[] url) throws ParseException {
  370. return new URLParser(url).parse();
  371. }
  372. /**
  373. * Parse an individual SIP message header from a string.
  374. *
  375. * @param header
  376. * String containing the SIP header.
  377. * @return a SIPHeader structure.
  378. * @exception ParseException
  379. * if there was an error parsing the message.
  380. */
  381. // public static SIPHeader parseSIPHeader(String header) throws ParseException {
  382. // int start = 0;
  383. // int end = header.length() - 1;
  384. // try {
  385. // // Squeeze out any leading control character.
  386. // while (header.charAt(start) <= 0x20)
  387. // start++;
  388. //
  389. // // Squeeze out any trailing control character.
  390. // while (header.charAt(end) <= 0x20)
  391. // end--;
  392. // }
  393. // catch (ArrayIndexOutOfBoundsException e) {
  394. // // Array contains only control char.
  395. // throw new ParseException("Empty header.", 0);
  396. // }
  397. //
  398. // StringBuilder buffer = new StringBuilder(end + 1);
  399. // int i = start;
  400. // int lineStart = start;
  401. // boolean endOfLine = false;
  402. // while (i <= end) {
  403. // char c = header.charAt(i);
  404. // if (c == '\r' || c == '\n') {
  405. // if (!endOfLine) {
  406. // buffer.append(header.substring(lineStart, i));
  407. // endOfLine = true;
  408. // }
  409. // }
  410. // else {
  411. // if (endOfLine) {
  412. // endOfLine = false;
  413. // if (c == ' ' || c == '\t') {
  414. // buffer.append(' ');
  415. // lineStart = i + 1;
  416. // }
  417. // else {
  418. // lineStart = i;
  419. // }
  420. // }
  421. // }
  422. //
  423. // i++;
  424. // }
  425. // buffer.append(header.substring(lineStart, i));
  426. // buffer.append('\n');
  427. //
  428. // HeaderParser hp = ParserFactory.createParser(buffer);
  429. // if (hp == null)
  430. // throw new ParseException("could not create parser", 0);
  431. // return hp.parse();
  432. // }
  433. /**
  434. * Parse the SIP Request Line
  435. *
  436. * @param requestLine
  437. * a String containing the request line to be parsed.
  438. * @return a RequestLine structure that has the parsed RequestLine
  439. * @exception ParseException
  440. * if there was an error parsing the requestLine.
  441. */
  442. public RequestLine parseSIPRequestLine(char[] requestLine)
  443. throws ParseException {
  444. // requestLine += "\n";
  445. char[] retval = new char[requestLine.length + 1];
  446. System.arraycopy(requestLine, 0, retval, 0, requestLine.length);
  447. retval[requestLine.length] = '\n';
  448. return new RequestLineParser(retval).parse();
  449. }
  450. /**
  451. * Parse the SIP Response message status line
  452. *
  453. * @param statusLine
  454. * a String containing the Status line to be parsed.
  455. * @return StatusLine class corresponding to message
  456. * @exception ParseException
  457. * if there was an error parsing
  458. * @see StatusLine
  459. */
  460. public StatusLine parseSIPStatusLine(char[] statusLine)
  461. throws ParseException {
  462. // statusLine += "\n";
  463. char[] retval = new char[statusLine.length + 1];
  464. System.arraycopy(statusLine, 0, retval, 0, statusLine.length);
  465. retval[statusLine.length] = '\n';
  466. return new StatusLineParser(statusLine).parse();
  467. }
  468. public static void setComputeContentLengthFromMessage(
  469. boolean computeContentLengthFromMessage) {
  470. CharsMsgParser.computeContentLengthFromMessage = computeContentLengthFromMessage;
  471. }
  472. /**
  473. * Test code.
  474. */
  475. public static void main(String[] args) throws ParseException {
  476. String messages[] = {
  477. "SIP/2.0 200 OK\r\n"
  478. + "To: \"The Little Blister\" <sip:LittleGuy@there.com>;tag=469bc066\r\n"
  479. + "From: \"The Master Blaster\" <sip:BigGuy@here.com>;tag=11\r\n"
  480. + "Via: SIP/2.0/UDP 139.10.134.246:5060;branch=z9hG4bK8b0a86f6_1030c7d18e0_17;received=139.10.134.246\r\n"
  481. + "Call-ID: 1030c7d18ae_a97b0b_b@8b0a86f6\r\n"
  482. + "CSeq: 1 SUBSCRIBE\r\n"
  483. + "Contact: <sip:172.16.11.162:5070>\r\n"
  484. + "Content-Length: 0\r\n\r\n",
  485. "SIP/2.0 180 Ringing\r\n"
  486. + "Via: SIP/2.0/UDP 172.18.1.29:5060;branch=z9hG4bK43fc10fb4446d55fc5c8f969607991f4\r\n"
  487. + "To: \"0440\" <sip:0440@212.209.220.131>;tag=2600\r\n"
  488. + "From: \"Andreas\" <sip:andreas@e-horizon.se>;tag=8524\r\n"
  489. + "Call-ID: f51a1851c5f570606140f14c8eb64fd3@172.18.1.29\r\n"
  490. + "CSeq: 1 INVITE\r\n" + "Max-Forwards: 70\r\n"
  491. + "Record-Route: <sip:212.209.220.131:5060>\r\n"
  492. + "Content-Length: 0\r\n\r\n",
  493. "REGISTER sip:nist.gov SIP/2.0\r\n"
  494. + "Via: SIP/2.0/UDP 129.6.55.182:14826\r\n"
  495. + "Max-Forwards: 70\r\n"
  496. + "From: <sip:mranga@nist.gov>;tag=6fcd5c7ace8b4a45acf0f0cd539b168b;epid=0d4c418ddf\r\n"
  497. + "To: <sip:mranga@nist.gov>\r\n"
  498. + "Call-ID: c5679907eb954a8da9f9dceb282d7230@129.6.55.182\r\n"
  499. + "CSeq: 1 REGISTER\r\n"
  500. + "Contact: <sip:129.6.55.182:14826>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER\"\r\n"
  501. + "User-Agent: RTC/(Microsoft RTC)\r\n"
  502. + "Event: registration\r\n"
  503. + "Allow-Events: presence\r\n"
  504. + "Content-Length: 0\r\n\r\n"
  505. + "INVITE sip:littleguy@there.com:5060 SIP/2.0\r\n"
  506. + "Via: SIP/2.0/UDP 65.243.118.100:5050\r\n"
  507. + "From: M. Ranganathan <sip:M.Ranganathan@sipbakeoff.com>;tag=1234\r\n"
  508. + "To: \"littleguy@there.com\" <sip:littleguy@there.com:5060> \r\n"
  509. + "Call-ID: Q2AboBsaGn9!?x6@sipbakeoff.com \r\n"
  510. + "CSeq: 1 INVITE \r\n"
  511. + "Content-Length: 247\r\n\r\n"
  512. + "v=0\r\n"
  513. + "o=4855 13760799956958020 13760799956958020 IN IP4 129.6.55.78\r\n"
  514. + "s=mysession session\r\n" + "p=+46 8 52018010\r\n"
  515. + "c=IN IP4 129.6.55.78\r\n" + "t=0 0\r\n"
  516. + "m=audio 6022 RTP/AVP 0 4 18\r\n"
  517. + "a=rtpmap:0 PCMU/8000\r\n"
  518. + "a=rtpmap:4 G723/8000\r\n"
  519. + "a=rtpmap:18 G729A/8000\r\n" + "a=ptime:20\r\n" };
  520. class ParserThread implements Runnable {
  521. String[] messages;
  522. public ParserThread(String[] messagesToParse) {
  523. this.messages = messagesToParse;
  524. }
  525. public void run() {
  526. for (int i = 0; i < messages.length; i++) {
  527. CharsMsgParser smp = new CharsMsgParser();
  528. try {
  529. SIPMessage sipMessage = smp
  530. .parseSIPMessage(messages[i].getBytes(), true, true, null);
  531. System.out.println(" i = " + i + " branchId = "
  532. + sipMessage.getTopmostVia().getBranch());
  533. // System.out.println("encoded " +
  534. // sipMessage.toString());
  535. } catch (ParseException ex) {
  536. ex.printStackTrace();
  537. }
  538. // System.out.println("dialog id = " +
  539. // sipMessage.getDialogId(false));
  540. }
  541. }
  542. }
  543. // for (int i = 0; i < 20; i++) {
  544. // new Thread(new ParserThread(messages)).start();
  545. // }
  546. new ParserThread(messages).run();
  547. }
  548. // public void setStrict(boolean strict) {
  549. // this.strict = strict;
  550. //
  551. // }
  552. //
  553. // public void setReadBody(boolean readBody) {
  554. // this.readBody = readBody;
  555. // }
  556. }