Package gov.nist.javax.sip.parser

Source Code of gov.nist.javax.sip.parser.URLParser

/*
* Conditions Of Use
*
* This software was developed by employees of the National Institute of
* Standards and Technology (NIST), an agency of the Federal Government.
* Pursuant to title 15 Untied States Code Section 105, works of NIST
* employees are not subject to copyright protection in the United States
* and are considered to be in the public domain.  As a result, a formal
* license is not needed to use the software.
*
* This software is provided by NIST as a service and is expressly
* provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY.  NIST does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
*
* Permission to use this software is contingent upon your acceptance
* of the terms of this agreement
*
* .
*
*/
package gov.nist.javax.sip.parser;
import gov.nist.core.HostNameParser;
import gov.nist.core.HostPort;
import gov.nist.core.NameValue;
import gov.nist.core.NameValueList;
import gov.nist.core.Token;
import gov.nist.javax.sip.address.GenericURI;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.address.TelURLImpl;
import gov.nist.javax.sip.address.TelephoneNumber;
import java.text.ParseException;

/**
* Parser For SIP and Tel URLs. Other kinds of URL's are handled by the
* J2SE 1.4 URL class.
* @version 1.2 $Revision: 1.25 $ $Date: 2009/03/13 23:03:29 $
*
* @author M. Ranganathan   <br/>
*
*
*/
public class URLParser extends Parser {

  public URLParser(String url) {
    this.lexer = new Lexer("sip_urlLexer", url);
  }

  // public tag added - issued by Miguel Freitas
  public URLParser(Lexer lexer) {
    this.lexer = lexer;
    this.lexer.selectLexer("sip_urlLexer");
  }
  protected static boolean isMark(char next) {
    switch (next) {
      case '-':
      case '_':
      case '.':
      case '!':
      case '~':
      case '*':
      case '\'':
      case '(':
      case ')':
        return true;
      default:
        return false;
    }
  }

  protected static boolean isUnreserved(char next) {
    return Lexer.isAlphaDigit(next) || isMark(next);
  }

  protected static boolean isReservedNoSlash(char next) {
    switch (next) {
      case ';':
      case '?':
      case ':':
      case '@':
      case '&':
      case '+':
      case '$':
      case ',':
        return true;
      default:
        return false;
    }
  }

  // Missing '=' bug in character set - discovered by interop testing
  // at SIPIT 13 by Bob Johnson and Scott Holben.
  // change . to ; by Bruno Konik
  protected static boolean isUserUnreserved(char la) {
    switch (la) {
      case '&':
      case '?':
      case '+':
      case '$':
      case '#':
      case '/':
      case ',':
      case ';':
      case '=':
        return true;
      default:
        return false;
    }
  }

  protected String unreserved() throws ParseException {
    char next = lexer.lookAhead(0);
    if (isUnreserved(next)) {
      lexer.consume(1);
      return String.valueOf(next);
    } else
      throw createParseException("unreserved");

  }

  /** Name or value of a parameter.
   */
  protected String paramNameOrValue() throws ParseException {
    int startIdx = lexer.getPtr();
    while (lexer.hasMoreChars()) {
      char next = lexer.lookAhead(0);
      boolean isValidChar = false;
      switch (next) {
        case '[':
        case ']'// JvB: fixed this one
        case '/':
        case ':':
        case '&':
        case '+':
        case '$':
          isValidChar = true;
      }
      if (isValidChar || isUnreserved(next)) {
        lexer.consume(1);
      } else if (isEscaped()) {
        lexer.consume(3);
      } else
        break;
    }
    return lexer.getBuffer().substring(startIdx, lexer.getPtr());
  }

  private NameValue uriParam() throws ParseException {
    if (debug)
      dbg_enter("uriParam");
    try {
      String pvalue = "";
      String pname = paramNameOrValue();
      char next = lexer.lookAhead(0);
      boolean isFlagParam = true;
      if (next == '=') {
        lexer.consume(1);
        pvalue = paramNameOrValue();
        isFlagParam = false;
      }
      if (pname.length() == 0 &&
        ( pvalue == null ||
        pvalue.length() == 0))
        return null;
      else return new NameValue(pname, pvalue, isFlagParam);
    } finally {
      if (debug)
        dbg_leave("uriParam");
    }
  }

  protected static boolean isReserved(char next) {
    switch (next) {
      case ';':
      case '/':
      case '?':
      case ':':
      case '=': // Bug fix by Bruno Konik
      case '@':
      case '&':
      case '+':
      case '$':
      case ',':
        return true;
      default:
        return false;
    }
  }

  protected String reserved() throws ParseException {
    char next = lexer.lookAhead(0);
    if (isReserved(next)) {
      lexer.consume(1);
      return new StringBuffer().append(next).toString();
    } else
      throw createParseException("reserved");
  }

  protected boolean isEscaped() {
    try {
      return lexer.lookAhead(0) == '%' &&
        Lexer.isHexDigit(lexer.lookAhead(1)) &&
        Lexer.isHexDigit(lexer.lookAhead(2));
    } catch (Exception ex) {
      return false;
    }
  }

  protected String escaped() throws ParseException {
    if (debug)
      dbg_enter("escaped");
    try {
      StringBuffer retval = new StringBuffer();
      char next = lexer.lookAhead(0);
      char next1 = lexer.lookAhead(1);
      char next2 = lexer.lookAhead(2);
      if (next == '%'
        && Lexer.isHexDigit(next1)
        && Lexer.isHexDigit(next2)) {
        lexer.consume(3);
        retval.append(next);
        retval.append(next1);
        retval.append(next2);
      } else
        throw createParseException("escaped");
      return retval.toString();
    } finally {
      if (debug)
        dbg_leave("escaped");
    }
  }

  protected String mark() throws ParseException {
    if (debug)
      dbg_enter("mark");
    try {
      char next = lexer.lookAhead(0);
      if (isMark(next)) {
        lexer.consume(1);
        return new String( new char[]{next} );
      } else
        throw createParseException("mark");
    } finally {
      if (debug)
        dbg_leave("mark");
    }
  }

  protected String uric() {
    if (debug)
      dbg_enter("uric");
    try {
      try {
        char la = lexer.lookAhead(0);
        if (isUnreserved(la)) {
          lexer.consume(1);
          return Lexer.charAsString(la);
        } else if (isReserved(la)) {
          lexer.consume(1);
          return Lexer.charAsString(la);
        } else if (isEscaped()) {
          String retval = lexer.charAsString(3);
          lexer.consume(3);
          return retval;
        } else
          return null;
      } catch (Exception ex) {
        return null;
      }
    } finally {
      if (debug)
        dbg_leave("uric");
    }

  }

  protected String uricNoSlash() {
    if (debug)
      dbg_enter("uricNoSlash");
    try {
      try {
        char la = lexer.lookAhead(0);
        if (isEscaped()) {
          String retval = lexer.charAsString(3);
          lexer.consume(3);
          return retval;
        } else if (isUnreserved(la)) {
          lexer.consume(1);
          return Lexer.charAsString(la);
        } else if (isReservedNoSlash(la)) {
          lexer.consume(1);
          return Lexer.charAsString(la);
        } else
          return null;
      } catch (ParseException ex) {
        return null;
      }
    } finally {
      if (debug)
        dbg_leave("uricNoSlash");
    }
  }

  protected String uricString() throws ParseException {
    StringBuffer retval = new StringBuffer();
    while (true) {
      String next = uric();
      if (next == null) {
        char la = lexer.lookAhead(0);
        // JvB: allow IPv6 addresses in generic URI strings
        // e.g. http://[::1]
        if ( la == '[' ) {
          HostNameParser hnp = new HostNameParser(this.getLexer());
          HostPort hp = hnp.hostPort( false );
          retval.append(hp.toString());
          continue;
        }
        break;
      }
      retval.append(next);
    }
    return retval.toString();
  }

  /**
   * Parse and return a structure for a generic URL.
   * Note that non SIP URLs are just stored as a string (not parsed).
   * @return URI is a URL structure for a SIP url.
   * @throws ParseException if there was a problem parsing.
   */
  public GenericURI uriReference() throws ParseException {
    if (debug)
      dbg_enter("uriReference");
    GenericURI retval = null;
    Token[] tokens = lexer.peekNextToken(2);
    Token t1 = (Token) tokens[0];
    Token t2 = (Token) tokens[1];
    try {

      if (t1.getTokenType() == TokenTypes.SIP ||
          t1.getTokenType() == TokenTypes.SIPS  ) {
        if (t2.getTokenType() == ':')
          retval = sipURL();
        else
          throw createParseException("Expecting \':\'");
      } else if (t1.getTokenType() == TokenTypes.TEL) {
        if (t2.getTokenType() == ':') {
          retval = telURL();
        } else
          throw createParseException("Expecting \':\'");
      } else {
        String urlString = uricString();
        try {
          retval = new GenericURI(urlString);
        } catch (ParseException ex) {
          throw createParseException(ex.getMessage());
        }
      }
    } finally {
      if (debug)
        dbg_leave("uriReference");
    }
    return retval;
  }

  /**
   * Parser for the base phone number.
   */
  private String base_phone_number() throws ParseException {
    StringBuffer s = new StringBuffer();

    if (debug)
      dbg_enter("base_phone_number");
    try {
      int lc = 0;
      while (lexer.hasMoreChars()) {
        char w = lexer.lookAhead(0);
        if (Lexer.isDigit(w)
          || w == '-'
          || w == '.'
          || w == '('
          || w == ')') {
          lexer.consume(1);
          s.append(w);
          lc++;
        } else if (lc > 0)
          break;
        else
          throw createParseException("unexpected " + w);
      }
      return s.toString();
    } finally {
      if (debug)
        dbg_leave("base_phone_number");
    }

  }

  /**
   * Parser for the local phone #.
   */
  private String local_number() throws ParseException {
    StringBuffer s = new StringBuffer();
    if (debug)
      dbg_enter("local_number");
    try {
      int lc = 0;
      while (lexer.hasMoreChars()) {
        char la = lexer.lookAhead(0);
        if (la == '*'
          || la == '#'
          || la == '-'
          || la == '.'
          || la == '('
          || la == ')'
            // JvB: allow 'A'..'F', should be uppercase
          || Lexer.isHexDigit(la)) {
          lexer.consume(1);
          s.append(la);
          lc++;
        } else if (lc > 0)
          break;
        else
          throw createParseException("unexepcted " + la);
      }
      return s.toString();
    } finally {
      if (debug)
        dbg_leave("local_number");
    }

  }

  /**
   * Parser for telephone subscriber.
   *
   * @return the parsed telephone number.
   */
  public final TelephoneNumber parseTelephoneNumber() throws ParseException {
    TelephoneNumber tn;

    if (debug)
      dbg_enter("telephone_subscriber");
    lexer.selectLexer("charLexer");
    try {
      char c = lexer.lookAhead(0);
      if (c == '+')
        tn = global_phone_number();
      else if (
        Lexer.isHexDigit(c// see RFC3966
          || c == '#'
          || c == '*'
          || c == '-'
          || c == '.'
          || c == '('
          || c == ')' ) {
        tn = local_phone_number();
      } else
        throw createParseException("unexpected char " + c);
      return tn;
    } finally {
      if (debug)
        dbg_leave("telephone_subscriber");
    }

  }

  private final TelephoneNumber global_phone_number() throws ParseException {
    if (debug)
      dbg_enter("global_phone_number");
    try {
      TelephoneNumber tn = new TelephoneNumber();
      tn.setGlobal(true);
      NameValueList nv = null;
      this.lexer.match(PLUS);
      String b = base_phone_number();
      tn.setPhoneNumber(b);
      if (lexer.hasMoreChars()) {
        char tok = lexer.lookAhead(0);
        if (tok == ';') {
          this.lexer.consume(1);
          nv = tel_parameters();
          tn.setParameters(nv);
        }
      }
      return tn;
    } finally {
      if (debug)
        dbg_leave("global_phone_number");
    }
  }

  private TelephoneNumber local_phone_number() throws ParseException {
    if (debug)
      dbg_enter("local_phone_number");
    TelephoneNumber tn = new TelephoneNumber();
    tn.setGlobal(false);
    NameValueList nv = null;
    String b = null;
    try {
      b = local_number();
      tn.setPhoneNumber(b);
      if (lexer.hasMoreChars()) {
        Token tok = this.lexer.peekNextToken();
        switch (tok.getTokenType()) {
          case SEMICOLON :
            {
              this.lexer.consume(1);
              nv = tel_parameters();
              tn.setParameters(nv);
              break;
            }
          default :
            {
              break;
            }
        }
      }
    } finally {
      if (debug)
        dbg_leave("local_phone_number");
    }
    return tn;
  }

  private NameValueList tel_parameters() throws ParseException {
    NameValueList nvList = new NameValueList();

    // JvB: Need to handle 'phone-context' specially
    // 'isub' (or 'ext') MUST appear first, but we accept any order here
    NameValue nv;
    while ( true ) {
      String pname = paramNameOrValue();

      // Handle 'phone-context' specially, it may start with '+'
      if ( pname.equalsIgnoreCase("phone-context")) {
        nv = phone_context();
      } else {
        if (lexer.lookAhead(0) == '=') {
          lexer.consume(1);
          String value = paramNameOrValue();
          nv = new NameValue( pname, value, false );
        } else {
          nv = new NameValue( pname, "", true )// flag param
        }
      }
      nvList.set( nv );

      if ( lexer.lookAhead(0) == ';' ) {
        lexer.consume(1);
      } else {
        return nvList;
      }
    }

  }

  /**
   * Parses the 'phone-context' parameter in tel: URLs
   * @throws ParseException
   */
  private NameValue phone_context() throws ParseException {
    lexer.match('=');

    char la = lexer.lookAhead(0);
    Object value;
    if (la=='+') {  // global-number-digits
      lexer.consume(1)// skip '+'
      value = "+" + base_phone_number();
    } else if ( Lexer.isAlphaDigit(la) ) {
      Token t = lexer.match( Lexer.ID )// more broad than allowed
      value = t.getTokenValue();
    } else {
      throw new ParseException( "Invalid phone-context:" + la , -1 );
    }
    return new NameValue( "phone-context", value, false );
  }

  /**
   * Parse and return a structure for a Tel URL.
   * @return a parsed tel url structure.
   */
  public TelURLImpl telURL() throws ParseException {
    lexer.match(TokenTypes.TEL);
    lexer.match(':');
    TelephoneNumber tn = this.parseTelephoneNumber();
    TelURLImpl telUrl = new TelURLImpl();
    telUrl.setTelephoneNumber(tn);
    return telUrl;

  }

  /**
   * Parse and return a structure for a SIP URL.
   * @return a URL structure for a SIP url.
   * @throws ParseException if there was a problem parsing.
   */
  public SipUri sipURL() throws ParseException {
    if (debug)
      dbg_enter("sipURL");
    SipUri retval = new SipUri();
    // pmusgrave - handle sips case
    Token nextToken = lexer.peekNextToken();
    int sipOrSips = TokenTypes.SIP;
    String scheme = TokenNames.SIP;
    if ( nextToken.getTokenType() == TokenTypes.SIPS)
    {
      sipOrSips = TokenTypes.SIPS;
      scheme = TokenNames.SIPS;
    }

    try {
      lexer.match(sipOrSips);
      lexer.match(':');
      retval.setScheme(scheme);
      int startOfUser = lexer.markInputPosition();
      String userOrHost = user()// Note: user may contain ';', host may not...
      String passOrPort = null;

      // name:password or host:port
      if ( lexer.lookAhead() == ':' ) {
        lexer.consume(1);
        passOrPort = password();
      }

      // name@hostPort
      if ( lexer.lookAhead() == '@' ) {
        lexer.consume(1);
        retval.setUser( userOrHost );
        if (passOrPort!=null) retval.setUserPassword( passOrPort );
      } else {
        // then userOrHost was a host, backtrack just in case a ';' was eaten...
        lexer.rewindInputPosition( startOfUser );
      }

      HostNameParser hnp = new HostNameParser(this.getLexer());
      HostPort hp = hnp.hostPort( false );
      retval.setHostPort(hp);

      lexer.selectLexer("charLexer");
      while (lexer.hasMoreChars()) {
        if (lexer.lookAhead(0) != ';')
          break;
        lexer.consume(1);
        NameValue parms = uriParam();
        if (parms != null) retval.setUriParameter(parms);
      }

      if (lexer.hasMoreChars() && lexer.lookAhead(0) == '?') {
        lexer.consume(1);
        while (lexer.hasMoreChars()) {
          NameValue parms = qheader();
          retval.setQHeader(parms);
          if (lexer.hasMoreChars() && lexer.lookAhead(0) != '&')
            break;
          else
            lexer.consume(1);
        }
      }
      return retval;
    } finally {
      if (debug)
        dbg_leave("sipURL");
    }
  }

  public String peekScheme() throws ParseException {
    Token[] tokens = lexer.peekNextToken(1);
    if (tokens.length == 0)
      return null;
    String scheme = ((Token) tokens[0]).getTokenValue();
    return scheme;
  }

  /**
   * Get a name value for a given query header (ie one that comes
   * after the ?).
   */
  protected NameValue qheader() throws ParseException {
    String name = lexer.getNextToken('=');
    lexer.consume(1);
    String value = hvalue();
    return new NameValue(name, value, false);

  }

  protected String hvalue() throws ParseException {
    StringBuffer retval = new StringBuffer();
    while (lexer.hasMoreChars()) {
      char la = lexer.lookAhead(0);
      // Look for a character that can terminate a URL.
      boolean isValidChar = false;
      switch (la) {
        case '+':
        case '?':
        case ':':
        case '[':
        case ']':
        case '/':
        case '$':
        case '_':
        case '-':
        case '"':
        case '!':
        case '~':
        case '*':
        case '.':
        case '(':
        case ')':
          isValidChar = true;
      }
      if (isValidChar || Lexer.isAlphaDigit(la)) {
        lexer.consume(1);
        retval.append(la);
      } else if (la == '%') {
        retval.append(escaped());
      } else
        break;
    }
    return retval.toString();
  }

  /**
   * Scan forward until you hit a terminating character for a URL.
   * We do not handle non sip urls in this implementation.
   * @return the string that takes us to the end of this URL (i.e. to
   * the next delimiter).
   */
  protected String urlString() throws ParseException {
    StringBuffer retval = new StringBuffer();
    lexer.selectLexer("charLexer");

    while (lexer.hasMoreChars()) {
      char la = lexer.lookAhead(0);
      // Look for a character that can terminate a URL.
      if (la == ' '
        || la == '\t'
        || la == '\n'
        || la == '>'
        || la == '<')
        break;
      lexer.consume(0);
      retval.append(la);
    }
    return retval.toString();
  }

  protected String user() throws ParseException {
    if (debug)
      dbg_enter("user");
    try {
      int startIdx = lexer.getPtr();
      while (lexer.hasMoreChars()) {
        char la = lexer.lookAhead(0);
        if (isUnreserved(la) || isUserUnreserved(la)) {
          lexer.consume(1);
        } else if (isEscaped()) {
          lexer.consume(3);
        } else
          break;
      }
      return lexer.getBuffer().substring(startIdx, lexer.getPtr());
    } finally {
      if (debug)
        dbg_leave("user");
    }

  }

  protected String password() throws ParseException {
    int startIdx = lexer.getPtr();
    while (true) {
      char la = lexer.lookAhead(0);
      boolean isValidChar = false;
      switch (la) {
        case '&':
        case '=':
        case '+':
        case '$':
        case ',':
          isValidChar = true;
      }
      if (isValidChar || isUnreserved(la)) {
        lexer.consume(1);
      } else if (isEscaped()) {
        lexer.consume(3); // bug reported by
                // Jeff Haynie
      } else
        break;

    }
    return lexer.getBuffer().substring(startIdx, lexer.getPtr());
  }

  /**
   * Default parse method. This method just calls uriReference.
   */
  public GenericURI parse() throws ParseException {
    return uriReference();
  }

  // quick test routine for debugging type assignment
  public static void main(String[] args) throws ParseException
  {
    // quick test for sips parsing
    String[] test = { "sip:alice@example.com",
          "sips:alice@examples.com" ,
          "sip:3Zqkv5dajqaaas0tCjCxT0xH2ZEuEMsFl0xoasip%3A%2B3519116786244%40siplab.domain.com@213.0.115.163:7070"};

    for ( int i = 0; i < test.length; i++)
    {
      URLParser p  = new URLParser(test[i]);

        GenericURI uri = p.parse();
        System.out.println("uri type returned " + uri.getClass().getName());
        System.out.println(test[i] + " is SipUri? " + uri.isSipURI()
            + ">" + uri.encode());
    }
  }

  /**

  **/
}
 
TOP

Related Classes of gov.nist.javax.sip.parser.URLParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.