• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Conditions Of Use
3 *
4 * This software was developed by employees of the National Institute of
5 * Standards and Technology (NIST), an agency of the Federal Government.
6 * Pursuant to title 15 Untied States Code Section 105, works of NIST
7 * employees are not subject to copyright protection in the United States
8 * and are considered to be in the public domain.  As a result, a formal
9 * license is not needed to use the software.
10 *
11 * This software is provided by NIST as a service and is expressly
12 * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
13 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
14 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
15 * AND DATA ACCURACY.  NIST does not warrant or make any representations
16 * regarding the use of the software or the results thereof, including but
17 * not limited to the correctness, accuracy, reliability or usefulness of
18 * the software.
19 *
20 * Permission to use this software is contingent upon your acceptance
21 * of the terms of this agreement
22 *
23 * .
24 *
25 */
26 package gov.nist.javax.sip.parser;
27 import gov.nist.core.HostNameParser;
28 import gov.nist.core.HostPort;
29 import gov.nist.core.NameValue;
30 import gov.nist.core.NameValueList;
31 import gov.nist.core.Token;
32 import gov.nist.javax.sip.address.GenericURI;
33 import gov.nist.javax.sip.address.SipUri;
34 import gov.nist.javax.sip.address.TelURLImpl;
35 import gov.nist.javax.sip.address.TelephoneNumber;
36 import java.text.ParseException;
37 
38 /**
39  * Parser For SIP and Tel URLs. Other kinds of URL's are handled by the
40  * J2SE 1.4 URL class.
41  * @version 1.2 $Revision: 1.27 $ $Date: 2009/10/22 10:27:39 $
42  *
43  * @author M. Ranganathan   <br/>
44  *
45  *
46  */
47 public class URLParser extends Parser {
48 
URLParser(String url)49     public URLParser(String url) {
50         this.lexer = new Lexer("sip_urlLexer", url);
51     }
52 
53     // public tag added - issued by Miguel Freitas
URLParser(Lexer lexer)54     public URLParser(Lexer lexer) {
55         this.lexer = lexer;
56         this.lexer.selectLexer("sip_urlLexer");
57     }
isMark(char next)58     protected static boolean isMark(char next) {
59         switch (next) {
60             case '-':
61             case '_':
62             case '.':
63             case '!':
64             case '~':
65             case '*':
66             case '\'':
67             case '(':
68             case ')':
69                 return true;
70             default:
71                 return false;
72         }
73     }
74 
isUnreserved(char next)75     protected static boolean isUnreserved(char next) {
76         return Lexer.isAlphaDigit(next) || isMark(next);
77     }
78 
isReservedNoSlash(char next)79     protected static boolean isReservedNoSlash(char next) {
80         switch (next) {
81             case ';':
82             case '?':
83             case ':':
84             case '@':
85             case '&':
86             case '+':
87             case '$':
88             case ',':
89                 return true;
90             default:
91                 return false;
92         }
93     }
94 
95     // Missing '=' bug in character set - discovered by interop testing
96     // at SIPIT 13 by Bob Johnson and Scott Holben.
97     // change . to ; by Bruno Konik
isUserUnreserved(char la)98     protected static boolean isUserUnreserved(char la) {
99         switch (la) {
100             case '&':
101             case '?':
102             case '+':
103             case '$':
104             case '#':
105             case '/':
106             case ',':
107             case ';':
108             case '=':
109                 return true;
110             default:
111                 return false;
112         }
113     }
114 
unreserved()115     protected String unreserved() throws ParseException {
116         char next = lexer.lookAhead(0);
117         if (isUnreserved(next)) {
118             lexer.consume(1);
119             return String.valueOf(next);
120         } else
121             throw createParseException("unreserved");
122 
123     }
124 
125     /** Name or value of a parameter.
126      */
paramNameOrValue()127     protected String paramNameOrValue() throws ParseException {
128         int startIdx = lexer.getPtr();
129         while (lexer.hasMoreChars()) {
130             char next = lexer.lookAhead(0);
131             boolean isValidChar = false;
132             switch (next) {
133                 case '[':
134                 case ']':// JvB: fixed this one
135                 case '/':
136                 case ':':
137                 case '&':
138                 case '+':
139                 case '$':
140                     isValidChar = true;
141             }
142             if (isValidChar || isUnreserved(next)) {
143                 lexer.consume(1);
144             } else if (isEscaped()) {
145                 lexer.consume(3);
146             } else
147                 break;
148         }
149         return lexer.getBuffer().substring(startIdx, lexer.getPtr());
150     }
151 
uriParam()152     private NameValue uriParam() throws ParseException {
153         if (debug)
154             dbg_enter("uriParam");
155         try {
156             String pvalue = "";
157             String pname = paramNameOrValue();
158             char next = lexer.lookAhead(0);
159             boolean isFlagParam = true;
160             if (next == '=') {
161                 lexer.consume(1);
162                 pvalue = paramNameOrValue();
163                 isFlagParam = false;
164             }
165             if (pname.length() == 0 &&
166                 ( pvalue == null ||
167                 pvalue.length() == 0))
168                 return null;
169             else return new NameValue(pname, pvalue, isFlagParam);
170         } finally {
171             if (debug)
172                 dbg_leave("uriParam");
173         }
174     }
175 
isReserved(char next)176     protected static boolean isReserved(char next) {
177         switch (next) {
178             case ';':
179             case '/':
180             case '?':
181             case ':':
182             case '=': // Bug fix by Bruno Konik
183             case '@':
184             case '&':
185             case '+':
186             case '$':
187             case ',':
188                 return true;
189             default:
190                 return false;
191         }
192     }
193 
reserved()194     protected String reserved() throws ParseException {
195         char next = lexer.lookAhead(0);
196         if (isReserved(next)) {
197             lexer.consume(1);
198             return new StringBuffer().append(next).toString();
199         } else
200             throw createParseException("reserved");
201     }
202 
isEscaped()203     protected boolean isEscaped() {
204         try {
205             return lexer.lookAhead(0) == '%' &&
206                 Lexer.isHexDigit(lexer.lookAhead(1)) &&
207                 Lexer.isHexDigit(lexer.lookAhead(2));
208         } catch (Exception ex) {
209             return false;
210         }
211     }
212 
escaped()213     protected String escaped() throws ParseException {
214         if (debug)
215             dbg_enter("escaped");
216         try {
217             StringBuffer retval = new StringBuffer();
218             char next = lexer.lookAhead(0);
219             char next1 = lexer.lookAhead(1);
220             char next2 = lexer.lookAhead(2);
221             if (next == '%'
222                 && Lexer.isHexDigit(next1)
223                 && Lexer.isHexDigit(next2)) {
224                 lexer.consume(3);
225                 retval.append(next);
226                 retval.append(next1);
227                 retval.append(next2);
228             } else
229                 throw createParseException("escaped");
230             return retval.toString();
231         } finally {
232             if (debug)
233                 dbg_leave("escaped");
234         }
235     }
236 
mark()237     protected String mark() throws ParseException {
238         if (debug)
239             dbg_enter("mark");
240         try {
241             char next = lexer.lookAhead(0);
242             if (isMark(next)) {
243                 lexer.consume(1);
244                 return new String( new char[]{next} );
245             } else
246                 throw createParseException("mark");
247         } finally {
248             if (debug)
249                 dbg_leave("mark");
250         }
251     }
252 
uric()253     protected String uric() {
254         if (debug)
255             dbg_enter("uric");
256         try {
257             try {
258                 char la = lexer.lookAhead(0);
259                 if (isUnreserved(la)) {
260                     lexer.consume(1);
261                     return Lexer.charAsString(la);
262                 } else if (isReserved(la)) {
263                     lexer.consume(1);
264                     return Lexer.charAsString(la);
265                 } else if (isEscaped()) {
266                     String retval = lexer.charAsString(3);
267                     lexer.consume(3);
268                     return retval;
269                 } else
270                     return null;
271             } catch (Exception ex) {
272                 return null;
273             }
274         } finally {
275             if (debug)
276                 dbg_leave("uric");
277         }
278 
279     }
280 
uricNoSlash()281     protected String uricNoSlash() {
282         if (debug)
283             dbg_enter("uricNoSlash");
284         try {
285             try {
286                 char la = lexer.lookAhead(0);
287                 if (isEscaped()) {
288                     String retval = lexer.charAsString(3);
289                     lexer.consume(3);
290                     return retval;
291                 } else if (isUnreserved(la)) {
292                     lexer.consume(1);
293                     return Lexer.charAsString(la);
294                 } else if (isReservedNoSlash(la)) {
295                     lexer.consume(1);
296                     return Lexer.charAsString(la);
297                 } else
298                     return null;
299             } catch (ParseException ex) {
300                 return null;
301             }
302         } finally {
303             if (debug)
304                 dbg_leave("uricNoSlash");
305         }
306     }
307 
uricString()308     protected String uricString() throws ParseException {
309         StringBuffer retval = new StringBuffer();
310         while (true) {
311             String next = uric();
312             if (next == null) {
313                 char la = lexer.lookAhead(0);
314                 // JvB: allow IPv6 addresses in generic URI strings
315                 // e.g. http://[::1]
316                 if ( la == '[' ) {
317                     HostNameParser hnp = new HostNameParser(this.getLexer());
318                     HostPort hp = hnp.hostPort( false );
319                     retval.append(hp.toString());
320                     continue;
321                 }
322                 break;
323             }
324             retval.append(next);
325         }
326         return retval.toString();
327     }
328 
329     /**
330      * Parse and return a structure for a generic URL.
331      * Note that non SIP URLs are just stored as a string (not parsed).
332      * @return URI is a URL structure for a SIP url.
333      * @throws ParseException if there was a problem parsing.
334      */
uriReference( boolean inBrackets )335     public GenericURI uriReference( boolean inBrackets ) throws ParseException {
336         if (debug)
337             dbg_enter("uriReference");
338         GenericURI retval = null;
339         Token[] tokens = lexer.peekNextToken(2);
340         Token t1 = (Token) tokens[0];
341         Token t2 = (Token) tokens[1];
342         try {
343 
344             if (t1.getTokenType() == TokenTypes.SIP ||
345                     t1.getTokenType() == TokenTypes.SIPS) {
346                 if (t2.getTokenType() == ':')
347                     retval = sipURL( inBrackets );
348                 else
349                     throw createParseException("Expecting \':\'");
350             } else if (t1.getTokenType() == TokenTypes.TEL) {
351                 if (t2.getTokenType() == ':') {
352                     retval = telURL( inBrackets );
353                 } else
354                     throw createParseException("Expecting \':\'");
355             } else {
356                 String urlString = uricString();
357                 try {
358                     retval = new GenericURI(urlString);
359                 } catch (ParseException ex) {
360                     throw createParseException(ex.getMessage());
361                 }
362             }
363         } finally {
364             if (debug)
365                 dbg_leave("uriReference");
366         }
367         return retval;
368     }
369 
370     /**
371      * Parser for the base phone number.
372      */
base_phone_number()373     private String base_phone_number() throws ParseException {
374         StringBuffer s = new StringBuffer();
375 
376         if (debug)
377             dbg_enter("base_phone_number");
378         try {
379             int lc = 0;
380             while (lexer.hasMoreChars()) {
381                 char w = lexer.lookAhead(0);
382                 if (Lexer.isDigit(w)
383                     || w == '-'
384                     || w == '.'
385                     || w == '('
386                     || w == ')') {
387                     lexer.consume(1);
388                     s.append(w);
389                     lc++;
390                 } else if (lc > 0)
391                     break;
392                 else
393                     throw createParseException("unexpected " + w);
394             }
395             return s.toString();
396         } finally {
397             if (debug)
398                 dbg_leave("base_phone_number");
399         }
400 
401     }
402 
403     /**
404      * Parser for the local phone #.
405      */
local_number()406     private String local_number() throws ParseException {
407         StringBuffer s = new StringBuffer();
408         if (debug)
409             dbg_enter("local_number");
410         try {
411             int lc = 0;
412             while (lexer.hasMoreChars()) {
413                 char la = lexer.lookAhead(0);
414                 if (la == '*'
415                     || la == '#'
416                     || la == '-'
417                     || la == '.'
418                     || la == '('
419                     || la == ')'
420                         // JvB: allow 'A'..'F', should be uppercase
421                     || Lexer.isHexDigit(la)) {
422                     lexer.consume(1);
423                     s.append(la);
424                     lc++;
425                 } else if (lc > 0)
426                     break;
427                 else
428                     throw createParseException("unexepcted " + la);
429             }
430             return s.toString();
431         } finally {
432             if (debug)
433                 dbg_leave("local_number");
434         }
435 
436     }
437 
438     /**
439      * Parser for telephone subscriber.
440      *
441      * @return the parsed telephone number.
442      */
parseTelephoneNumber( boolean inBrackets )443     public final TelephoneNumber parseTelephoneNumber( boolean inBrackets )
444     	throws ParseException {
445         TelephoneNumber tn;
446 
447         if (debug)
448             dbg_enter("telephone_subscriber");
449         lexer.selectLexer("charLexer");
450         try {
451             char c = lexer.lookAhead(0);
452             if (c == '+')
453                 tn = global_phone_number( inBrackets );
454             else if (
455                 Lexer.isHexDigit(c)// see RFC3966
456                     || c == '#'
457                     || c == '*'
458                     || c == '-'
459                     || c == '.'
460                     || c == '('
461                     || c == ')' ) {
462                 tn = local_phone_number( inBrackets );
463             } else
464                 throw createParseException("unexpected char " + c);
465             return tn;
466         } finally {
467             if (debug)
468                 dbg_leave("telephone_subscriber");
469         }
470 
471     }
472 
global_phone_number( boolean inBrackets )473     private final TelephoneNumber global_phone_number( boolean inBrackets ) throws ParseException {
474         if (debug)
475             dbg_enter("global_phone_number");
476         try {
477             TelephoneNumber tn = new TelephoneNumber();
478             tn.setGlobal(true);
479             NameValueList nv = null;
480             this.lexer.match(PLUS);
481             String b = base_phone_number();
482             tn.setPhoneNumber(b);
483             if (lexer.hasMoreChars()) {
484                 char tok = lexer.lookAhead(0);
485                 if (tok == ';' && inBrackets) {
486                     this.lexer.consume(1);
487                     nv = tel_parameters();
488                     tn.setParameters(nv);
489                 }
490             }
491             return tn;
492         } finally {
493             if (debug)
494                 dbg_leave("global_phone_number");
495         }
496     }
497 
local_phone_number( boolean inBrackets )498     private TelephoneNumber local_phone_number( boolean inBrackets ) throws ParseException {
499         if (debug)
500             dbg_enter("local_phone_number");
501         TelephoneNumber tn = new TelephoneNumber();
502         tn.setGlobal(false);
503         NameValueList nv = null;
504         String b = null;
505         try {
506             b = local_number();
507             tn.setPhoneNumber(b);
508             if (lexer.hasMoreChars()) {
509                 Token tok = this.lexer.peekNextToken();
510                 switch (tok.getTokenType()) {
511                     case SEMICOLON:
512                         {
513                         	if (inBrackets) {
514                         		this.lexer.consume(1);
515                         		nv = tel_parameters();
516                         		tn.setParameters(nv);
517                         	}
518                             break;
519                         }
520                     default :
521                         {
522                             break;
523                         }
524                 }
525             }
526         } finally {
527             if (debug)
528                 dbg_leave("local_phone_number");
529         }
530         return tn;
531     }
532 
tel_parameters()533     private NameValueList tel_parameters() throws ParseException {
534         NameValueList nvList = new NameValueList();
535 
536         // JvB: Need to handle 'phone-context' specially
537         // 'isub' (or 'ext') MUST appear first, but we accept any order here
538         NameValue nv;
539         while ( true ) {
540             String pname = paramNameOrValue();
541 
542             // Handle 'phone-context' specially, it may start with '+'
543             if ( pname.equalsIgnoreCase("phone-context")) {
544                 nv = phone_context();
545             } else {
546                 if (lexer.lookAhead(0) == '=') {
547                     lexer.consume(1);
548                     String value = paramNameOrValue();
549                     nv = new NameValue( pname, value, false );
550                 } else {
551                     nv = new NameValue( pname, "", true );// flag param
552                 }
553             }
554             nvList.set( nv );
555 
556             if ( lexer.lookAhead(0) == ';' ) {
557                 lexer.consume(1);
558             } else {
559                 return nvList;
560             }
561         }
562 
563     }
564 
565     /**
566      * Parses the 'phone-context' parameter in tel: URLs
567      * @throws ParseException
568      */
phone_context()569     private NameValue phone_context() throws ParseException {
570         lexer.match('=');
571 
572         char la = lexer.lookAhead(0);
573         Object value;
574         if (la=='+') {// global-number-digits
575             lexer.consume(1);// skip '+'
576             value = "+" + base_phone_number();
577         } else if ( Lexer.isAlphaDigit(la) ) {
578             Token t = lexer.match( Lexer.ID );// more broad than allowed
579             value = t.getTokenValue();
580         } else {
581             throw new ParseException( "Invalid phone-context:" + la , -1 );
582         }
583         return new NameValue( "phone-context", value, false );
584     }
585 
586     /**
587      * Parse and return a structure for a Tel URL.
588      * @return a parsed tel url structure.
589      */
telURL( boolean inBrackets )590     public TelURLImpl telURL( boolean inBrackets ) throws ParseException {
591         lexer.match(TokenTypes.TEL);
592         lexer.match(':');
593         TelephoneNumber tn = this.parseTelephoneNumber(inBrackets);
594         TelURLImpl telUrl = new TelURLImpl();
595         telUrl.setTelephoneNumber(tn);
596         return telUrl;
597 
598     }
599 
600     /**
601      * Parse and return a structure for a SIP URL.
602      * @return a URL structure for a SIP url.
603      * @throws ParseException if there was a problem parsing.
604      */
sipURL( boolean inBrackets )605     public SipUri sipURL( boolean inBrackets ) throws ParseException {
606         if (debug)
607             dbg_enter("sipURL");
608         SipUri retval = new SipUri();
609         // pmusgrave - handle sips case
610         Token nextToken = lexer.peekNextToken();
611         int sipOrSips = TokenTypes.SIP;
612         String scheme = TokenNames.SIP;
613         if ( nextToken.getTokenType() == TokenTypes.SIPS)
614         {
615             sipOrSips = TokenTypes.SIPS;
616             scheme = TokenNames.SIPS;
617         }
618 
619         try {
620             lexer.match(sipOrSips);
621             lexer.match(':');
622             retval.setScheme(scheme);
623             int startOfUser = lexer.markInputPosition();
624             String userOrHost = user();// Note: user may contain ';', host may not...
625             String passOrPort = null;
626 
627             // name:password or host:port
628             if ( lexer.lookAhead() == ':' ) {
629                 lexer.consume(1);
630                 passOrPort = password();
631             }
632 
633             // name@hostPort
634             if ( lexer.lookAhead() == '@' ) {
635                 lexer.consume(1);
636                 retval.setUser( userOrHost );
637                 if (passOrPort!=null) retval.setUserPassword( passOrPort );
638             } else {
639                 // then userOrHost was a host, backtrack just in case a ';' was eaten...
640                 lexer.rewindInputPosition( startOfUser );
641             }
642 
643             HostNameParser hnp = new HostNameParser(this.getLexer());
644             HostPort hp = hnp.hostPort( false );
645             retval.setHostPort(hp);
646 
647             lexer.selectLexer("charLexer");
648             while (lexer.hasMoreChars()) {
649             	// If the URI is not enclosed in brackets, parameters belong to header
650                 if (lexer.lookAhead(0) != ';' || !inBrackets)
651                     break;
652                 lexer.consume(1);
653                 NameValue parms = uriParam();
654                 if (parms != null) retval.setUriParameter(parms);
655             }
656 
657             if (lexer.hasMoreChars() && lexer.lookAhead(0) == '?') {
658                 lexer.consume(1);
659                 while (lexer.hasMoreChars()) {
660                     NameValue parms = qheader();
661                     retval.setQHeader(parms);
662                     if (lexer.hasMoreChars() && lexer.lookAhead(0) != '&')
663                         break;
664                     else
665                         lexer.consume(1);
666                 }
667             }
668             return retval;
669         // BEGIN android-added
670         } catch (RuntimeException e) {
671             throw new ParseException("Invalid URL: " + lexer.getBuffer(), -1);
672         // END android-added
673         } finally {
674             if (debug)
675                 dbg_leave("sipURL");
676         }
677     }
678 
peekScheme()679     public String peekScheme() throws ParseException {
680         Token[] tokens = lexer.peekNextToken(1);
681         if (tokens.length == 0)
682             return null;
683         String scheme = ((Token) tokens[0]).getTokenValue();
684         return scheme;
685     }
686 
687     /**
688      * Get a name value for a given query header (ie one that comes
689      * after the ?).
690      */
qheader()691     protected NameValue qheader() throws ParseException {
692         String name = lexer.getNextToken('=');
693         lexer.consume(1);
694         String value = hvalue();
695         return new NameValue(name, value, false);
696 
697     }
698 
hvalue()699     protected String hvalue() throws ParseException {
700         StringBuffer retval = new StringBuffer();
701         while (lexer.hasMoreChars()) {
702             char la = lexer.lookAhead(0);
703             // Look for a character that can terminate a URL.
704             boolean isValidChar = false;
705             switch (la) {
706                 case '+':
707                 case '?':
708                 case ':':
709                 case '[':
710                 case ']':
711                 case '/':
712                 case '$':
713                 case '_':
714                 case '-':
715                 case '"':
716                 case '!':
717                 case '~':
718                 case '*':
719                 case '.':
720                 case '(':
721                 case ')':
722                     isValidChar = true;
723             }
724             if (isValidChar || Lexer.isAlphaDigit(la)) {
725                 lexer.consume(1);
726                 retval.append(la);
727             } else if (la == '%') {
728                 retval.append(escaped());
729             } else
730                 break;
731         }
732         return retval.toString();
733     }
734 
735     /**
736      * Scan forward until you hit a terminating character for a URL.
737      * We do not handle non sip urls in this implementation.
738      * @return the string that takes us to the end of this URL (i.e. to
739      * the next delimiter).
740      */
urlString()741     protected String urlString() throws ParseException {
742         StringBuffer retval = new StringBuffer();
743         lexer.selectLexer("charLexer");
744 
745         while (lexer.hasMoreChars()) {
746             char la = lexer.lookAhead(0);
747             // Look for a character that can terminate a URL.
748             if (la == ' '
749                 || la == '\t'
750                 || la == '\n'
751                 || la == '>'
752                 || la == '<')
753                 break;
754             lexer.consume(0);
755             retval.append(la);
756         }
757         return retval.toString();
758     }
759 
user()760     protected String user() throws ParseException {
761         if (debug)
762             dbg_enter("user");
763         try {
764             int startIdx = lexer.getPtr();
765             while (lexer.hasMoreChars()) {
766                 char la = lexer.lookAhead(0);
767                 if (isUnreserved(la) || isUserUnreserved(la)) {
768                     lexer.consume(1);
769                 } else if (isEscaped()) {
770                     lexer.consume(3);
771                 } else
772                     break;
773             }
774             return lexer.getBuffer().substring(startIdx, lexer.getPtr());
775         } finally {
776             if (debug)
777                 dbg_leave("user");
778         }
779 
780     }
781 
password()782     protected String password() throws ParseException {
783         int startIdx = lexer.getPtr();
784         while (true) {
785             char la = lexer.lookAhead(0);
786             boolean isValidChar = false;
787             switch (la) {
788                 case '&':
789                 case '=':
790                 case '+':
791                 case '$':
792                 case ',':
793                     isValidChar = true;
794             }
795             if (isValidChar || isUnreserved(la)) {
796                 lexer.consume(1);
797             } else if (isEscaped()) {
798                 lexer.consume(3); // bug reported by
799                                 // Jeff Haynie
800             } else
801                 break;
802 
803         }
804         return lexer.getBuffer().substring(startIdx, lexer.getPtr());
805     }
806 
807     /**
808      * Default parse method. This method just calls uriReference.
809      */
parse()810     public GenericURI parse() throws ParseException {
811         return uriReference( true );
812     }
813 
814     // quick test routine for debugging type assignment
main(String[] args)815     public static void main(String[] args) throws ParseException
816     {
817         // quick test for sips parsing
818         String[] test = { "sip:alice@example.com",
819                     "sips:alice@examples.com" ,
820                     "sip:3Zqkv5dajqaaas0tCjCxT0xH2ZEuEMsFl0xoasip%3A%2B3519116786244%40siplab.domain.com@213.0.115.163:7070"};
821 
822         for ( int i = 0; i < test.length; i++)
823         {
824             URLParser p  = new URLParser(test[i]);
825 
826                 GenericURI uri = p.parse();
827                 System.out.println("uri type returned " + uri.getClass().getName());
828                 System.out.println(test[i] + " is SipUri? " + uri.isSipURI()
829                         + ">" + uri.encode());
830         }
831     }
832 
833     /**
834 
835     **/
836 }
837 
838