parser/parse_uri.c
2bb60634
 /*
c32feee5
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
  * This file is part of ser, a free SIP server.
  *
  * ser is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
b2fcef3c
  *
  * History:
  * --------
  * 2003-04-04  convenience inbound-uri parser parse_orig_ruri
  *             introduced (jiri)
4b50480e
  * 2003-04-11  new parse_uri introduced (better, parses also some parameters,
  *              works in one pass) (andrei)
f2e456c3
  * 2003-04-11  ser_error is now set in parse_uri (andrei)
af67aa14
  * 2003-04-26  ZSW (jiri)
3f601bd0
  * 2003-07-03  sips:, r2, lr=on support added (andrei)
2518c900
  * 2005-02-25  preliminary tel uri support (andrei)
591011ae
  * 2005-03-03  more tel uri fixes (andrei)
7a068ff3
  * 2006-04-20  comp uri param. support (rfc3486) if defined USE_COMP  (andrei)
face488e
  * 2007-09-10  introduced phone2tel option which allows NOT to consider
  *             user=phone URIs as TEL URIs
  *
2bb60634
  */
 
1eb01c66
 /** @file
  * @brief Parser :: Parse URI's
  *
  * @ingroup parser
  */
 
7dd0b342
 
face488e
 #include "../globals.h"
2bb60634
 #include "parse_uri.h"
 #include <string.h>
 #include "../dprint.h"
4b50480e
 #include "../ut.h"   /* q_memchr */
2bb60634
 #include "../error.h"
6b109fa2
 #include "../core_stats.h"
2bb60634
 
 /* buf= pointer to begining of uri (sip:x@foo.bar:5060;a=b?h=i)
  * len= len of uri
  * returns: fills uri & returns <0 on error or 0 if ok 
  */
4b50480e
 int parse_uri(char* buf, int len, struct sip_uri* uri)
 {
 	enum states  {	URI_INIT, URI_USER, URI_PASSWORD, URI_PASSWORD_ALPHA,
 					URI_HOST, URI_HOST_P,
 					URI_HOST6_P, URI_HOST6_END, URI_PORT, 
 					URI_PARAM, URI_PARAM_P, URI_VAL_P, URI_HEADERS,
 					/* param states */
 					/* transport */
 					PT_T, PT_R, PT_A, PT_N, PT_S, PT_P, PT_O, PT_R2, PT_T2,
 					PT_eq,
 					/* ttl */
 					      PTTL_T2, PTTL_L, PTTL_eq,
 					/* user */
 					PU_U, PU_S, PU_E, PU_R, PU_eq,
 					/* method */
 					PM_M, PM_E, PM_T, PM_H, PM_O, PM_D, PM_eq,
 					/* maddr */
 					      PMA_A, PMA_D, PMA_D2, PMA_R, PMA_eq,
 					/* lr */
3f601bd0
 					PLR_L, PLR_R_FIN, PLR_eq,
 					/* r2 */
 					PR2_R, PR2_2_FIN, PR2_eq,
1dd73047
 					/* gr */
 					PGR_G, PGR_R_FIN, PGR_eq,
7a068ff3
 #ifdef USE_COMP
 					/* comp */
 					PCOMP_C, PCOMP_O, PCOMP_M, PCOMP_P, PCOMP_eq,
 					
 					/* comp values */
 					/* sigcomp */
 					VCOMP_S, VCOMP_SIGC_I, VCOMP_SIGC_G,
 					VCOMP_SIGC_C, VCOMP_SIGC_O,  VCOMP_SIGC_M,
 					VCOMP_SIGC_P_FIN,
 					/* sergz */
 							VCOMP_SGZ_E, VCOMP_SGZ_R, VCOMP_SGZ_G,
 							VCOMP_SGZ_Z_FIN,
 #endif
3f601bd0
 					
4b50480e
 					/* transport values */
 					/* udp */
 					VU_U, VU_D, VU_P_FIN,
 					/* tcp */
 					VT_T, VT_C, VT_P_FIN,
 					/* tls */
 					      VTLS_L, VTLS_S_FIN,
 					/* sctp */
8393efff
 					VS_S, VS_C, VS_T, VS_P_FIN,
 					/* ws */
 					VW_W, VW_S_FIN
4b50480e
 	};
350a47c1
 	register enum states state;
4b50480e
 	char* s;
 	char* b; /* param start */
 	char *v; /* value start */
 	str* param; /* current param */
 	str* param_val; /* current param val */
 	str user;
 	str password;
 	int port_no;
350a47c1
 	register char* p;
4b50480e
 	char* end;
 	char* pass;
 	int found_user;
 	int error_headers;
2518c900
 	unsigned int scheme;
9b1ad373
 	uri_type backup_urit;
 	uri_flags backup_urif;
 
7a068ff3
 #ifdef USE_COMP
 	str comp_str; /* not returned for now */
 	str comp_val; /* not returned for now */
 #endif
2518c900
 	
 #define SIP_SCH		0x3a706973
 #define SIPS_SCH	0x73706973
 #define TEL_SCH		0x3a6c6574
fecefaf1
 #define URN_SCH		0x3a6e7275
4b50480e
 	
 #define case_port( ch, var) \
 	case ch: \
 			 (var)=(var)*10+ch-'0'; \
 			 break
 			 
 #define still_at_user  \
 						if (found_user==0){ \
 							user.s=uri->host.s; \
 							if (pass){\
 								user.len=pass-user.s; \
 								password.s=pass+1; \
 								password.len=p-password.s; \
 							}else{ \
 								user.len=p-user.s; \
 							}\
591011ae
 							/* save the uri type/scheme */ \
9b1ad373
 							backup_urit=uri->type; \
 							backup_urif=uri->flags; \
4b50480e
 							/* everything else is 0 */ \
 							memset(uri, 0, sizeof(struct sip_uri)); \
9b1ad373
 							/* restore the scheme & flags, copy user & pass */ \
 							uri->type=backup_urit; \
 							uri->flags=backup_urif; \
4b50480e
 							uri->user=user; \
 							if (pass)	uri->passwd=password;  \
 							s=p+1; \
 							found_user=1;\
 							error_headers=0; \
 							state=URI_HOST; \
 						}else goto error_bad_char 
 
 #define check_host_end \
 					case ':': \
 						/* found the host */ \
 						uri->host.s=s; \
 						uri->host.len=p-s; \
 						state=URI_PORT; \
 						s=p+1; \
 						break; \
 					case ';': \
 						uri->host.s=s; \
 						uri->host.len=p-s; \
 						state=URI_PARAM; \
 						s=p+1; \
 						break; \
 					case '?': \
 						uri->host.s=s; \
 						uri->host.len=p-s; \
 						state=URI_HEADERS; \
 						s=p+1; \
 						break; \
 					case '&': \
 					case '@': \
 						goto error_bad_char 
 
 
 #define param_set(t_start, v_start) \
 					param->s=(t_start);\
 					param->len=(p-(t_start));\
 					param_val->s=(v_start); \
 					param_val->len=(p-(v_start)) 
 
 #define semicolon_case \
 					case';': \
 						if (pass){ \
 							found_user=1;/* no user, pass cannot contain ';'*/ \
 							pass=0; \
 						} \
 						state=URI_PARAM   /* new param */ 
 
 #define question_case \
 					case '?': \
 						uri->params.s=s; \
 						uri->params.len=p-s; \
 						state=URI_HEADERS; \
 						s=p+1; \
 						if (pass){ \
 							found_user=1;/* no user, pass cannot contain '?'*/ \
 							pass=0; \
 						}
 
 #define colon_case \
 					case ':': \
 						if (found_user==0){ \
 							/*might be pass but only if user not found yet*/ \
 							if (pass){ \
 								found_user=1; /* no user */ \
 								pass=0; \
 							}else{ \
 								pass=p; \
 							} \
 						} \
 						state=URI_PARAM_P /* generic param */
 
 #define param_common_cases \
 					case '@': \
 						/* ughhh, this is still the user */ \
 						still_at_user; \
 						break; \
 					semicolon_case; \
 						break; \
 					question_case; \
 						break; \
 					colon_case; \
 						break
 
 #define value_common_cases \
 					case '@': \
 						/* ughhh, this is still the user */ \
 						still_at_user; \
 						break; \
 					semicolon_case; \
 						param_set(b, v); \
 						break; \
 					question_case; \
 						param_set(b, v); \
 						break; \
 					colon_case; \
 						state=URI_VAL_P; \
 						break
 
 #define param_switch(old_state, c1, c2, new_state) \
 			case old_state: \
 				switch(*p){ \
 					case c1: \
 					case c2: \
 						state=(new_state); \
 						break; \
 					param_common_cases; \
 					default: \
 						state=URI_PARAM_P; \
 				} \
 				break
 #define param_switch1(old_state, c1, new_state) \
 			case old_state: \
 				switch(*p){ \
 					case c1: \
 						state=(new_state); \
 						break; \
 					param_common_cases; \
 					default: \
 						state=URI_PARAM_P; \
 				} \
 				break
 #define param_switch_big(old_state, c1, c2, d1, d2, new_state_c, new_state_d) \
 			case old_state : \
 				switch(*p){ \
 					case c1: \
 					case c2: \
 						state=(new_state_c); \
 						break; \
 					case d1: \
 					case d2: \
 						state=(new_state_d); \
 						break; \
 					param_common_cases; \
 					default: \
 						state=URI_PARAM_P; \
 				} \
 				break
 #define value_switch(old_state, c1, c2, new_state) \
 			case old_state: \
 				switch(*p){ \
 					case c1: \
 					case c2: \
 						state=(new_state); \
 						break; \
 					value_common_cases; \
 					default: \
 						state=URI_VAL_P; \
 				} \
 				break
 #define value_switch_big(old_state, c1, c2, d1, d2, new_state_c, new_state_d) \
 			case old_state: \
 				switch(*p){ \
 					case c1: \
 					case c2: \
 						state=(new_state_c); \
 						break; \
 					case d1: \
 					case d2: \
 						state=(new_state_d); \
 						break; \
 					value_common_cases; \
 					default: \
 						state=URI_VAL_P; \
 				} \
 				break
 
 #define transport_fin(c_state, proto_no) \
 			case c_state: \
 				switch(*p){ \
 					case '@': \
 						still_at_user; \
 						break; \
 					semicolon_case; \
 						param_set(b, v); \
 						uri->proto=(proto_no); \
 						break; \
 					question_case; \
 						param_set(b, v); \
 						uri->proto=(proto_no); \
 						break; \
 					colon_case;  \
 					default: \
 						state=URI_VAL_P; \
 						break; \
 				} \
 				break
 			
7a068ff3
 
 #ifdef USE_COMP
 #define comp_fin(c_state, comp_no) \
 			case c_state: \
 				switch(*p){ \
 					case '@': \
 						still_at_user; \
 						break; \
 					semicolon_case; \
 						/* param_set(b, v); */ \
 						uri->comp=(comp_no); \
 						break; \
 					question_case; \
 						/* param_set(b, v) */; \
 						uri->comp=(comp_no); \
 						break; \
 					colon_case;  \
 					default: \
 						state=URI_VAL_P; \
 						break; \
 				} \
 				break
 			
 #endif
4b50480e
 
 	/* init */
 	end=buf+len;
 	p=buf+4;
 	found_user=0;
 	error_headers=0;
 	b=v=0;
 	param=param_val=0;
 	pass=0;
e0931fb0
 	password.s=0; /* fixes gcc 4.0 warning */
 	password.len=0;
4b50480e
 	port_no=0;
 	state=URI_INIT;
 	memset(uri, 0, sizeof(struct sip_uri)); /* zero it all, just to be sure*/
fecefaf1
 	/*look for sip:, sips: ,tel: or urn:*/
3f601bd0
 	if (len<5) goto error_too_short;
2518c900
 	scheme=buf[0]+(buf[1]<<8)+(buf[2]<<16)+(buf[3]<<24);
 	scheme|=0x20202020;
 	if (scheme==SIP_SCH){
 		uri->type=SIP_URI_T;
 	}else if(scheme==SIPS_SCH){
 		if(buf[4]==':'){ p++; uri->type=SIPS_URI_T;}
3f601bd0
 		else goto error_bad_uri;
2518c900
 	}else if (scheme==TEL_SCH){
 		uri->type=TEL_URI_T;
fecefaf1
 	}else if (scheme==URN_SCH){
 		uri->type=URN_URI_T;
2518c900
 	}else goto error_bad_uri;
4b50480e
 	
 	s=p;
 	for(;p<end; p++){
350a47c1
 		switch((unsigned char)state){
4b50480e
 			case URI_INIT:
 				switch(*p){
 					case '[':
 						/* uri =  [ipv6address]... */
 						state=URI_HOST6_P;
 						s=p;
 						break;
 					case ']':
 						/* invalid, no uri can start with ']' */
 					case ':':
 						/* the same as above for ':' */
 						goto error_bad_char;
 					case '@': /* error no user part, or
 								 be forgiving and accept it ? */
 					default:
 						state=URI_USER;
 				}
 				break; 
 			case URI_USER:
 				switch(*p){
 					case '@':
 						/* found the user*/
 						uri->user.s=s;
 						uri->user.len=p-s;
 						state=URI_HOST;
 						found_user=1;
 						s=p+1; /* skip '@' */
 						break;
 					case ':':
 						/* found the user, or the host? */
 						uri->user.s=s;
 						uri->user.len=p-s;
 						state=URI_PASSWORD;
 						s=p+1; /* skip ':' */
 						break;
 					case ';':
 						/* this could be still the user or
 						 * params?*/
 						uri->host.s=s;
 						uri->host.len=p-s;
 						state=URI_PARAM;
 						s=p+1;
 						break;
 					case '?': /* still user or headers? */
 						uri->host.s=s;
 						uri->host.len=p-s;
 						state=URI_HEADERS;
 						s=p+1;
 						break;
 						/* almost anything permitted in the user part */
9b1ad373
 					case '.':
 					case '-':
 					case '(':
 					case ')':
 						/* tel uri visual separators, set flag meaning, that
 						 * user should be normalized before usage
 						 */
 						uri->flags|=URI_USER_NORMALIZE;
 						break;
4b50480e
 					case '[':
 					case ']': /* the user part cannot contain "[]" */
 						goto error_bad_char;
 				}
 				break;
 			case URI_PASSWORD: /* this can also be the port (missing user)*/
 				switch(*p){
 					case '@':
 						/* found the password*/
 						uri->passwd.s=s;
 						uri->passwd.len=p-s;
 						port_no=0;
 						state=URI_HOST;
 						found_user=1;
 						s=p+1; /* skip '@' */
 						break;
 					case ';':
 						/* upps this is the port */
 						uri->port.s=s;
 						uri->port.len=p-s;
 						uri->port_no=port_no;
 						/* user contains in fact the host */
 						uri->host.s=uri->user.s;
 						uri->host.len=uri->user.len;
 						uri->user.s=0;
 						uri->user.len=0;
 						state=URI_PARAM;
 						found_user=1; /*  there is no user part */
 						s=p+1;
 						break;
 					case '?':
 						/* upps this is the port */
 						uri->port.s=s;
 						uri->port.len=p-s;
 						uri->port_no=port_no;
 						/* user contains in fact the host */
 						uri->host.s=uri->user.s;
 						uri->host.len=uri->user.len;
 						uri->user.s=0;
 						uri->user.len=0;
 						state=URI_HEADERS;
 						found_user=1; /*  there is no user part */
 						s=p+1;
 						break;
 					case_port('0', port_no);
 					case_port('1', port_no);
 					case_port('2', port_no);
 					case_port('3', port_no);
 					case_port('4', port_no);
 					case_port('5', port_no);
 					case_port('6', port_no);
 					case_port('7', port_no);
 					case_port('8', port_no);
 					case_port('9', port_no);
 					case '[':
 					case ']':
 					case ':':
 						goto error_bad_char;
 					default:
 						/* it can't be the port, non number found */
 						port_no=0;
 						state=URI_PASSWORD_ALPHA;
 				}
 				break;
 			case URI_PASSWORD_ALPHA:
 				switch(*p){
 					case '@':
 						/* found the password*/
 						uri->passwd.s=s;
 						uri->passwd.len=p-s;
 						state=URI_HOST;
 						found_user=1;
 						s=p+1; /* skip '@' */
 						break;
 					case ';': /* contains non-numbers => cannot be port no*/
 					case '?':
 						goto error_bad_port;
 					case '[':
 					case ']':
 					case ':':
 						goto error_bad_char;
 				}
 				break;
 			case URI_HOST:
 				switch(*p){
 					case '[':
 						state=URI_HOST6_P;
 						break;
 					case ':': 
 					case ';':
 					case '?': /* null host name ->invalid */
 					case '&':
 					case '@': /*chars not allowed in hosts names */
 						goto error_bad_host;
 					default:
 						state=URI_HOST_P;
 				}
 				break;
 			case URI_HOST_P:
 				switch(*p){
 					check_host_end;
 				}
 				break;
 			case URI_HOST6_END:
 				switch(*p){
 					check_host_end;
 					default: /*no chars allowed after [ipv6] */
 						goto error_bad_host;
 				}
 				break;
 			case URI_HOST6_P:
 				switch(*p){
 					case ']':
 						state=URI_HOST6_END;
 						break;
 					case '[':
 					case '&':
 					case '@':
 					case ';':
 					case '?':
 						goto error_bad_host;
 				}
 				break;
 			case URI_PORT:
 				switch(*p){
 					case ';':
 						uri->port.s=s;
 						uri->port.len=p-s;
 						uri->port_no=port_no;
 						state=URI_PARAM;
 						s=p+1;
 						break;
 					case '?':
 						uri->port.s=s;
 						uri->port.len=p-s;
 						uri->port_no=port_no;
 						state=URI_HEADERS;
 						s=p+1;
 						break;
 					case_port('0', port_no);
 					case_port('1', port_no);
 					case_port('2', port_no);
 					case_port('3', port_no);
 					case_port('4', port_no);
 					case_port('5', port_no);
 					case_port('6', port_no);
 					case_port('7', port_no);
 					case_port('8', port_no);
 					case_port('9', port_no);
 					case '&':
 					case '@':
 					case ':':
 					default:
 						goto error_bad_port;
 				}
 				break;
 			case URI_PARAM: /* beginning of a new param */
 				switch(*p){
 					param_common_cases;
 					/* recognized params */
 					case 't':
 					case 'T':
 						b=p;
 						state=PT_T;
 						break;
 					case 'u':
 					case 'U':
 						b=p;
 						state=PU_U;
 						break;
 					case 'm':
 					case 'M':
 						b=p;
 						state=PM_M;
 						break;
 					case 'l':
 					case 'L':
 						b=p;
 						state=PLR_L;
 						break;
3f601bd0
 					case 'r':
 					case 'R':
 						b=p;
 						state=PR2_R;
8f0e1af3
 						break;
1dd73047
 					case 'g':
 					case 'G':
 						b=p;
 						state=PGR_G;
 						break;
7a068ff3
 #ifdef USE_COMP
 					case 'c':
 					case 'C':
 						b=p;
 						state=PCOMP_C;
 						break;
 #endif
4b50480e
 					default:
 						state=URI_PARAM_P;
 				}
 				break;
 			case URI_PARAM_P: /* ignore current param */
 				/* supported params:
3f601bd0
 				 *  maddr, transport, ttl, lr, user, method, r2  */
4b50480e
 				switch(*p){
 					param_common_cases;
 				};
 				break;
 			/* ugly but fast param names parsing */
 			/*transport */
 			param_switch_big(PT_T,  'r', 'R', 't', 'T', PT_R, PTTL_T2);
 			param_switch(PT_R,  'a', 'A', PT_A);
 			param_switch(PT_A,  'n', 'N', PT_N);
 			param_switch(PT_N,  's', 'S', PT_S);
 			param_switch(PT_S,  'p', 'P', PT_P);
 			param_switch(PT_P,  'o', 'O', PT_O);
 			param_switch(PT_O,  'r', 'R', PT_R2);
 			param_switch(PT_R2, 't', 'T', PT_T2);
 			param_switch1(PT_T2, '=',  PT_eq);
 			/* value parsing */
 			case PT_eq:
 				param=&uri->transport;
 				param_val=&uri->transport_val;
 				switch (*p){
 					param_common_cases;
 					case 'u':
 					case 'U':
 						v=p;
 						state=VU_U;
 						break;
 					case 't':
 					case 'T':
 						v=p;
 						state=VT_T;
 						break;
 					case 's':
 					case 'S':
 						v=p;
 						state=VS_S;
 						break;
8393efff
 					case 'w':
 					case 'W':
 						v=p;
 						state=VW_W;
 						break;
4b50480e
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
 				/* generic value */
 			case URI_VAL_P:
 				switch(*p){
 					value_common_cases;
 				}
 				break;
 			/* udp */
 			value_switch(VU_U,  'd', 'D', VU_D);
 			value_switch(VU_D,  'p', 'P', VU_P_FIN);
 			transport_fin(VU_P_FIN, PROTO_UDP);
 			/* tcp */
 			value_switch_big(VT_T,  'c', 'C', 'l', 'L', VT_C, VTLS_L);
 			value_switch(VT_C,  'p', 'P', VT_P_FIN);
 			transport_fin(VT_P_FIN, PROTO_TCP);
 			/* tls */
 			value_switch(VTLS_L, 's', 'S', VTLS_S_FIN);
 			transport_fin(VTLS_S_FIN, PROTO_TLS);
 			/* sctp */
 			value_switch(VS_S, 'c', 'C', VS_C);
 			value_switch(VS_C, 't', 'T', VS_T);
 			value_switch(VS_T, 'p', 'P', VS_P_FIN);
 			transport_fin(VS_P_FIN, PROTO_SCTP);
8393efff
 			/* ws */
 			value_switch(VW_W, 's', 'S', VW_S_FIN);
 			transport_fin(VW_S_FIN, PROTO_WS);
4b50480e
 			
 			/* ttl */
 			param_switch(PTTL_T2,  'l', 'L', PTTL_L);
 			param_switch1(PTTL_L,  '=', PTTL_eq);
 			case PTTL_eq:
 				param=&uri->ttl;
 				param_val=&uri->ttl_val;
 				switch(*p){
 					param_common_cases;
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
 			
 			/* user param */
 			param_switch(PU_U, 's', 'S', PU_S);
 			param_switch(PU_S, 'e', 'E', PU_E);
 			param_switch(PU_E, 'r', 'R', PU_R);
 			param_switch1(PU_R, '=', PU_eq);
 			case PU_eq:
 				param=&uri->user_param;
 				param_val=&uri->user_param_val;
 				switch(*p){
 					param_common_cases;
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
 			
 			/* method*/
 			param_switch_big(PM_M, 'e', 'E', 'a', 'A', PM_E, PMA_A);
 			param_switch(PM_E, 't', 'T', PM_T);
 			param_switch(PM_T, 'h', 'H', PM_H);
 			param_switch(PM_H, 'o', 'O', PM_O);
 			param_switch(PM_O, 'd', 'D', PM_D);
 			param_switch1(PM_D, '=', PM_eq);
 			case PM_eq:
 				param=&uri->method;
 				param_val=&uri->method_val;
 				switch(*p){
 					param_common_cases;
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
 
 			/*maddr*/
 			param_switch(PMA_A,  'd', 'D', PMA_D);
 			param_switch(PMA_D,  'd', 'D', PMA_D2);
 			param_switch(PMA_D2, 'r', 'R', PMA_R);
 			param_switch1(PMA_R, '=', PMA_eq);
 			case PMA_eq:
 				param=&uri->maddr;
 				param_val=&uri->maddr_val;
 				switch(*p){
 					param_common_cases;
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
 			
 			/* lr */
 			param_switch(PLR_L,  'r', 'R', PLR_R_FIN);
 			case PLR_R_FIN:
 				switch(*p){
 					case '@':
 						still_at_user; 
 						break;
3f601bd0
 					case '=':
 						state=PLR_eq;
 						break;
4b50480e
 					semicolon_case; 
 						uri->lr.s=b;
 						uri->lr.len=(p-b);
 						break;
 					question_case; 
 						uri->lr.s=b;
 						uri->lr.len=(p-b);
 						break;
 					colon_case;
 						break;
 					default:
 						state=URI_PARAM_P;
 				}
 				break;
3f601bd0
 				/* handle lr=something case */
 			case PLR_eq:
 				param=&uri->lr;
 				param_val=&uri->lr_val;
 				switch(*p){
 					param_common_cases;
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
 			/* r2 */
 			param_switch1(PR2_R,  '2', PR2_2_FIN);
 			case PR2_2_FIN:
 				switch(*p){
 					case '@':
 						still_at_user; 
 						break;
 					case '=':
 						state=PR2_eq;
 						break;
 					semicolon_case; 
 						uri->r2.s=b;
 						uri->r2.len=(p-b);
 						break;
 					question_case; 
 						uri->r2.s=b;
 						uri->r2.len=(p-b);
 						break;
 					colon_case;
 						break;
 					default:
 						state=URI_PARAM_P;
 				}
 				break;
 				/* handle lr=something case */
 			case PR2_eq:
 				param=&uri->r2;
 				param_val=&uri->r2_val;
 				switch(*p){
 					param_common_cases;
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
1dd73047
 			/* gr */
 			param_switch(PGR_G,  'r', 'R', PGR_R_FIN);
 			case PGR_R_FIN:
 				switch(*p){
 					case '@':
 						still_at_user;
 						break;
 					case '=':
 						state=PGR_eq;
 						break;
 					semicolon_case;
 						uri->gr.s=b;
 						uri->gr.len=(p-b);
 						break;
 					question_case;
 						uri->gr.s=b;
 						uri->gr.len=(p-b);
 						break;
 					colon_case;
 						break;
 					default:
 						state=URI_PARAM_P;
 				}
 				break;
 				/* handle gr=something case */
 			case PGR_eq:
 				param=&uri->gr;
 				param_val=&uri->gr_val;
 				switch(*p){
 					param_common_cases;
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
 
7a068ff3
 #ifdef USE_COMP
 			param_switch(PCOMP_C,  'o', 'O' , PCOMP_O);
 			param_switch(PCOMP_O,  'm', 'M' , PCOMP_M);
 			param_switch(PCOMP_M,  'p', 'P' , PCOMP_P);
 			param_switch1(PCOMP_P,  '=', PCOMP_eq);
 			/* value */
 			case PCOMP_eq:
 				param=&comp_str;
 				param_val=&comp_val;
 				switch (*p){
 					param_common_cases;
 					case 's':
 					case 'S':
 						v=p;
 						state=VCOMP_S;
 						break;
 					default:
 						v=p;
 						state=URI_VAL_P;
 				}
 				break;
 			/* sigcomp*/
 			value_switch_big(VCOMP_S, 'i', 'I', 'e', 'E',
 									VCOMP_SIGC_I, VCOMP_SGZ_E);
 			value_switch(VCOMP_SIGC_I, 'g', 'G', VCOMP_SIGC_G);
 			value_switch(VCOMP_SIGC_G, 'c', 'C', VCOMP_SIGC_C);
 			value_switch(VCOMP_SIGC_C, 'o', 'O', VCOMP_SIGC_O);
 			value_switch(VCOMP_SIGC_O, 'm', 'M', VCOMP_SIGC_M);
 			value_switch(VCOMP_SIGC_M, 'p', 'P', VCOMP_SIGC_P_FIN);
 			comp_fin(VCOMP_SIGC_P_FIN, COMP_SIGCOMP);
 			
 			/* sergz*/
 			value_switch(VCOMP_SGZ_E, 'r', 'R', VCOMP_SGZ_R);
 			value_switch(VCOMP_SGZ_R, 'g', 'G', VCOMP_SGZ_G);
 			value_switch(VCOMP_SGZ_G, 'z', 'Z', VCOMP_SGZ_Z_FIN);
 			comp_fin(VCOMP_SGZ_Z_FIN, COMP_SERGZ);
 #endif
 				
4b50480e
 				
 				
 			case URI_HEADERS:
 				/* for now nobody needs them so we completely ignore the 
 				 * headers (they are not allowed in request uri) --andrei */
 				switch(*p){
 					case '@':
 						/* yak, we are still at user */
 						still_at_user;
 						break;
 					case ';':
 						/* we might be still parsing user, try it */
 						if (found_user) goto error_bad_char;
 						error_headers=1; /* if this is not the user
 											we have an error */
 						/* if pass is set => it cannot be user:pass
 						 * => error (';') is illegal in a header */
 						if (pass) goto error_headers;
 						break;
 					case ':':
 						if (found_user==0){
 							/*might be pass but only if user not found yet*/
 							if (pass){
 								found_user=1; /* no user */
 								pass=0;
 							}else{
 								pass=p;
 							}
 						}
 						break;
 					case '?':
 						if (pass){
c32feee5
 							found_user=1; /* no user, pass cannot contain '?'*/
4b50480e
 							pass=0;
 						}
 						break;
 				}
 				break;
 			default:
 				goto error_bug;
 		}
 	}
 	/*end of uri */
 	switch (state){
c32feee5
 		case URI_INIT: /* error empty uri */
4b50480e
 			goto error_too_short;
 		case URI_USER:
 			/* this is the host, it can't be the user */
 			if (found_user) goto error_bad_uri;
 			uri->host.s=s;
 			uri->host.len=p-s;
 			state=URI_HOST;
 			break;
 		case URI_PASSWORD:
 			/* this is the port, it can't be the passwd */
 			if (found_user) goto error_bad_port;
 			uri->port.s=s;
 			uri->port.len=p-s;
 			uri->port_no=port_no;
 			uri->host=uri->user;
 			uri->user.s=0;
 			uri->user.len=0;
 			break;
 		case URI_PASSWORD_ALPHA:
fecefaf1
 			/* it might be an urn, check scheme and set host */
 			if (scheme==URN_SCH){
 				uri->host.s=s;
 				uri->host.len=p-s;
 				DBG("parsed urn scheme...\n");
4b50480e
 			/* this is the port, it can't be the passwd */
fecefaf1
 			}else goto error_bad_port;
4b50480e
 		case URI_HOST_P:
 		case URI_HOST6_END:
 			uri->host.s=s;
 			uri->host.len=p-s;
 			break;
 		case URI_HOST: /* error: null host */
 		case URI_HOST6_P: /* error: unterminated ipv6 reference*/
 			goto error_bad_host;
 		case URI_PORT:
 			uri->port.s=s;
 			uri->port.len=p-s;
 			uri->port_no=port_no;
 			break;
 		case URI_PARAM:
 		case URI_PARAM_P:
 		/* intermediate param states */
 		case PT_T: /* transport */
 		case PT_R:
 		case PT_A:
 		case PT_N:
 		case PT_S:
 		case PT_P:
 		case PT_O:
 		case PT_R2:
 		case PT_T2:
 		case PT_eq: /* ignore empty transport params */
 		case PTTL_T2: /* ttl */
 		case PTTL_L:
 		case PTTL_eq:
 		case PU_U:  /* user */
 		case PU_S:
 		case PU_E:
 		case PU_R:
 		case PU_eq:
 		case PM_M: /* method */
 		case PM_E:
 		case PM_T:
 		case PM_H:
 		case PM_O:
 		case PM_D:
 		case PM_eq:
 		case PLR_L: /* lr */
1dd73047
 		case PR2_R: /* r2 */
 		case PGR_G: /* gr */
7a068ff3
 #ifdef USE_COMP
 		case PCOMP_C:
 		case PCOMP_O:
 		case PCOMP_M:
 		case PCOMP_P:
 		case PCOMP_eq:
 #endif
4b50480e
 			uri->params.s=s;
 			uri->params.len=p-s;
 			break;
 		/* fin param states */
 		case PLR_R_FIN:
3f601bd0
 		case PLR_eq:
4b50480e
 			uri->params.s=s;
 			uri->params.len=p-s;
 			uri->lr.s=b;
 			uri->lr.len=p-b;
 			break;
3f601bd0
 		case PR2_2_FIN:
 		case PR2_eq:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			uri->r2.s=b;
 			uri->r2.len=p-b;
 			break;
1dd73047
 		case PGR_R_FIN:
 		case PGR_eq:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			uri->gr.s=b;
 			uri->gr.len=p-b;
 			break;
4b50480e
 		case URI_VAL_P:
 		/* intermediate value states */
 		case VU_U:
 		case VU_D:
 		case VT_T:
 		case VT_C:
 		case VTLS_L:
 		case VS_S:
 		case VS_C:
 		case VS_T:
8393efff
 		case VW_W:
4b50480e
 			uri->params.s=s;
 			uri->params.len=p-s;
 			param_set(b, v);
 			break;
7a068ff3
 #ifdef USE_COMP
 		case VCOMP_S:
 		case VCOMP_SIGC_I:
 		case VCOMP_SIGC_G:
 		case VCOMP_SIGC_C:
 		case VCOMP_SIGC_O:
 		case VCOMP_SIGC_M:
 		case VCOMP_SGZ_E:
 		case VCOMP_SGZ_R:
 		case VCOMP_SGZ_G:
 			/* unrecognized comp method, assume none */
 			uri->params.s=s;
 			uri->params.len=p-s;
 			/* uri->comp=COMP_NONE ; */
 			break;
 #endif
4b50480e
 		/* fin value states */
 		case VU_P_FIN:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			param_set(b, v);
 			uri->proto=PROTO_UDP;
 			break;
 		case VT_P_FIN:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			param_set(b, v);
 			uri->proto=PROTO_TCP;
 			break;
 		case VTLS_S_FIN:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			param_set(b, v);
 			uri->proto=PROTO_TLS;
 			break;
 		case VS_P_FIN:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			param_set(b, v);
 			uri->proto=PROTO_SCTP;
 			break;
8393efff
 		case VW_S_FIN:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			param_set(b, v);
 			uri->proto=PROTO_WS;
 			break;
7a068ff3
 #ifdef USE_COMP
 		case VCOMP_SIGC_P_FIN:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			/* param_set(b, v); */
 			uri->comp=COMP_SIGCOMP;
 			break;
 		case VCOMP_SGZ_Z_FIN:
 			uri->params.s=s;
 			uri->params.len=p-s;
 			/* param_set(b, v); */
 			uri->comp=COMP_SERGZ;
 			break;
 #endif
4b50480e
 		/* headers */
 		case URI_HEADERS:
 			uri->headers.s=s;
 			uri->headers.len=p-s;
 			if (error_headers) goto error_headers;
 			break;
 		default:
 			goto error_bug;
 	}
f924ae07
 	switch(uri->type){
9b1ad373
 		case SIPS_URI_T:
f924ae07
 		case SIP_URI_T:
773f8568
 			/* save the original sip: URI parameters in sip_params */
 			uri->sip_params=uri->params;
face488e
 			if ((phone2tel) &&
 			     (uri->user_param_val.len == 5) &&
 				 (strncmp(uri->user_param_val.s, "phone", 5) == 0)
 				) {
f924ae07
 				uri->type = TEL_URI_T;
9b1ad373
 				uri->flags |= URI_SIP_USER_PHONE;
591011ae
 				/* move params from user into uri->params */
 				p=q_memchr(uri->user.s, ';', uri->user.len);
 				if (p){
9b1ad373
 					/* NOTE: 
 					 * specialized uri params (user, maddr, etc.) still hold
 					 * the values from the sip-uri envelope
 					 * while uri->params point to the params in the embedded tel uri
 					 */
591011ae
 					uri->params.s=p+1;
 					uri->params.len=uri->user.s+uri->user.len-uri->params.s;
 					uri->user.len=p-uri->user.s;
9b1ad373
 				} else {
773f8568
 					uri->params.s=0;
9b1ad373
 					uri->params.len=0;
591011ae
 				}
9b1ad373
 			} else {
 				uri->flags&=~URI_USER_NORMALIZE;
f924ae07
 			}
 			break;
 		case TEL_URI_T:
 		case TELS_URI_T:
 			/* fix tel uris, move the number in uri and empty the host */
 			uri->user=uri->host;
 			uri->host.s="";
 			uri->host.len=0;
 			break;
fecefaf1
 		/* urn: do nothing */
 		case URN_URI_T:
 			break;
f924ae07
 		case ERROR_URI_T:
591011ae
 			LOG(L_ERR, "ERROR: parse_uri unexpected error (BUG?)\n"); 
 			goto error_bad_uri;
f924ae07
 			break; /* do nothing, avoids a compilation warning */
2518c900
 	}
13fd48f8
 
 	if(uri->port.len>5)
 		goto error_invalid_port;
 
2518c900
 #ifdef EXTRA_DEBUG
4b50480e
 	/* do stuff */
2518c900
 	DBG("parsed uri:\n type=%d user=<%.*s>(%d)\n passwd=<%.*s>(%d)\n"
 			" host=<%.*s>(%d)\n port=<%.*s>(%d): %d\n params=<%.*s>(%d)\n"
 			" headers=<%.*s>(%d)\n",
 			uri->type,
6b6f226f
 			uri->user.len, ZSW(uri->user.s), uri->user.len,
 			uri->passwd.len, ZSW(uri->passwd.s), uri->passwd.len,
 			uri->host.len, ZSW(uri->host.s), uri->host.len,
 			uri->port.len, ZSW(uri->port.s), uri->port.len, uri->port_no,
 			uri->params.len, ZSW(uri->params.s), uri->params.len,
 			uri->headers.len, ZSW(uri->headers.s), uri->headers.len
4b50480e
 		);
9b1ad373
 	DBG(" uri flags : ");
 		if (uri->flags & URI_USER_NORMALIZE) DBG("user_need_norm ");
 		if (uri->flags & URI_SIP_USER_PHONE) DBG("sip_user_phone ");
 		DBG("   value=%d\n",uri->flags);
4b50480e
 	DBG(" uri params:\n   transport=<%.*s>, val=<%.*s>, proto=%d\n",
6b6f226f
 			uri->transport.len, ZSW(uri->transport.s), uri->transport_val.len,
683fdd6f
 			ZSW(uri->transport_val.s), uri->proto);
4b50480e
 	DBG("   user-param=<%.*s>, val=<%.*s>\n",
6b6f226f
 			uri->user_param.len, ZSW(uri->user_param.s), 
 			uri->user_param_val.len, ZSW(uri->user_param_val.s));
4b50480e
 	DBG("   method=<%.*s>, val=<%.*s>\n",
6b6f226f
 			uri->method.len, ZSW(uri->method.s), 
 			uri->method_val.len, ZSW(uri->method_val.s));
4b50480e
 	DBG("   ttl=<%.*s>, val=<%.*s>\n",
6b6f226f
 			uri->ttl.len, ZSW(uri->ttl.s), 
 			uri->ttl_val.len, ZSW(uri->ttl_val.s));
4b50480e
 	DBG("   maddr=<%.*s>, val=<%.*s>\n",
6b6f226f
 			uri->maddr.len, ZSW(uri->maddr.s), 
 			uri->maddr_val.len, ZSW(uri->maddr_val.s));
 	DBG("   lr=<%.*s>\n", uri->lr.len, ZSW(uri->lr.s)); 
8f0e1af3
 	DBG("   r2=<%.*s>\n", uri->r2.len, ZSW(uri->r2.s));
7a068ff3
 #ifdef USE_COMP
 	DBG("   comp=%d\n", uri->comp);
 #endif
 
af67aa14
 #endif
4b50480e
 	return 0;
 	
 error_too_short:
0f0e9bd3
 	DBG("parse_uri: uri too short: <%.*s> (%d)\n",
 		len, ZSW(buf), len);
591011ae
 	goto error_exit;
4b50480e
 error_bad_char:
0f0e9bd3
 	DBG("parse_uri: bad char '%c' in state %d"
 		" parsed: <%.*s> (%d) / <%.*s> (%d)\n",
 		*p, state, (int)(p-buf), ZSW(buf), (int)(p-buf),
 		len, ZSW(buf), len);
591011ae
 	goto error_exit;
4b50480e
 error_bad_host:
0f0e9bd3
 	DBG("parse_uri: bad host in uri (error at char %c in"
 		" state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n",
 		*p, state, (int)(p-buf), ZSW(buf), (int)(p-buf),
 		len, ZSW(buf), len);
591011ae
 	goto error_exit;
4b50480e
 error_bad_port:
0f0e9bd3
 	DBG("parse_uri: bad port in uri (error at char %c in"
 		" state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n",
 		*p, state, (int)(p-buf), ZSW(buf), (int)(p-buf),
 		len, ZSW(buf), len);
591011ae
 	goto error_exit;
13fd48f8
 error_invalid_port:
 	DBG("parse_uri: bad port in uri: [%.*s] in <%.*s>\n",
 			uri->port.len, uri->port.s, len, ZSW(buf));
 	goto error_exit;
4b50480e
 error_bad_uri:
0f0e9bd3
 	DBG("parse_uri: bad uri,  state %d"
 		" parsed: <%.*s> (%d) / <%.*s> (%d)\n",
 		state, (int)(p-buf), ZSW(buf), (int)(p-buf), len,
 		ZSW(buf), len);
591011ae
 	goto error_exit;
4b50480e
 error_headers:
0f0e9bd3
 	DBG("parse_uri: bad uri headers: <%.*s>(%d)"
 		" / <%.*s>(%d)\n",
 		uri->headers.len, ZSW(uri->headers.s), uri->headers.len,
 		len, ZSW(buf), len);
591011ae
 	goto error_exit;
4b50480e
 error_bug:
 	LOG(L_CRIT, "BUG: parse_uri: bad  state %d"
 			" parsed: <%.*s> (%d) / <%.*s> (%d)\n",
6b6f226f
 			 state, (int)(p-buf), ZSW(buf), (int)(p-buf), len, ZSW(buf), len);
591011ae
 error_exit:
f2e456c3
 	ser_error=E_BAD_URI;
591011ae
 	uri->type=ERROR_URI_T;
6b109fa2
 	STATS_BAD_URI();
f2e456c3
 	return E_BAD_URI;
4b50480e
 }
 
2bb60634
 
b2fcef3c
 static inline int _parse_ruri(str *uri,
 	int *status, struct sip_uri *parsed_uri)
 {
 	if (*status) return 1;
 
 	if (parse_uri(uri->s, uri->len, parsed_uri)<0) {
 		LOG(L_ERR, "ERROR: _parse_ruri: bad uri <%.*s>\n", 
6b6f226f
 				uri->len, ZSW(uri->s));
b2fcef3c
 		*status=0;
 		return -1;
 	}
 	*status=1;
 	return 1;
 }
855c2e68
 
 int parse_sip_msg_uri(struct sip_msg* msg)
 {
 	char* tmp;
 	int tmp_len;
 	if (msg->parsed_uri_ok) return 1;
b2fcef3c
 
 	if (msg->new_uri.s){
 		tmp=msg->new_uri.s;
 		tmp_len=msg->new_uri.len;
 	}else{
 		tmp=msg->first_line.u.request.uri.s;
 		tmp_len=msg->first_line.u.request.uri.len;
855c2e68
 	}
b2fcef3c
 	if (parse_uri(tmp, tmp_len, &msg->parsed_uri)<0){
47e9d6d8
 		DBG("ERROR: parse_sip_msg_uri: bad uri <%.*s>\n",
 			tmp_len, tmp);
b2fcef3c
 		msg->parsed_uri_ok=0;
 		return -1;
 	}
 	msg->parsed_uri_ok=1;
 	return 1;
855c2e68
 }
 
b2fcef3c
 int parse_orig_ruri(struct sip_msg* msg)
 {
 	int ret;
 
 	ret=_parse_ruri(&REQ_LINE(msg).uri,
 		&msg->parsed_orig_ruri_ok, &msg->parsed_orig_ruri);
 	if (ret<0) LOG(L_ERR, "ERROR: parse_orig_ruri failed\n");
 	return ret;
 }
9b1ad373
 
 inline int normalize_tel_user(char* res, str* src) {
 	int i, l;
 	l=0;
 	for (i=0; i<src->len; i++) {
 		switch (src->s[i]) {
 			case '-':
 			case '.':
 			case '(':
 			case ')':
 				break;
 			default:
 				res[l++]=src->s[i];
 		}
 	}  
 	return l;
 }
 
 
536065b8
 str	s_sip  = STR_STATIC_INIT("sip");
 str	s_sips = STR_STATIC_INIT("sips");
 str	s_tel  = STR_STATIC_INIT("tel");
 str	s_tels = STR_STATIC_INIT("tels");
fecefaf1
 str	s_urn  = STR_STATIC_INIT("urn");
9b1ad373
 static str	s_null = STR_STATIC_INIT("");
 
 inline void uri_type_to_str(uri_type type, str *s) {
 	switch (type) {
 	case SIP_URI_T:
 		*s = s_sip;
 		break;
 	case SIPS_URI_T:
 		*s = s_sips;
 		break;
 	case TEL_URI_T:
 		*s = s_tel;
 		break;
 	case TELS_URI_T:
 		*s = s_tels;
 		break;
fecefaf1
 	case URN_URI_T:
 		*s = s_urn;
 		break;
9b1ad373
 	default:
 		*s = s_null;
 	}
 }
 
f90ef683
 static str	s_udp  = STR_STATIC_INIT("udp");
 static str	s_tcp  = STR_STATIC_INIT("tcp");
 static str	s_tls  = STR_STATIC_INIT("tls");
 static str	s_sctp = STR_STATIC_INIT("sctp");
8393efff
 static str	s_ws   = STR_STATIC_INIT("ws");
f90ef683
 
 inline void proto_type_to_str(unsigned short type, str *s) {
 	switch (type) {
 	case PROTO_UDP:
 		*s = s_udp;
 		break;
 	case PROTO_TCP:
 		*s = s_tcp;
 		break;
 	case PROTO_TLS:
 		*s = s_tls;
 		break;
 	case PROTO_SCTP:
 		*s = s_sctp;
 		break;
8393efff
 	case PROTO_WS:
1ccd1f68
 	case PROTO_WSS:
8393efff
 		*s = s_ws;
 		break;
f90ef683
 	default:
 		*s = s_null;
 	}
 }