parser/parse_param.c
420b6bb0
 /* 
  * Generic Parameter Parser
  *
c32feee5
  * Copyright (C) 2001-2003 FhG Fokus
420b6bb0
  *
  * 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
  *
  * 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 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * History:
  * -------
  * 2003-03-24 Created by janakj
ed49bd28
  * 2003-04-07 shm duplication added (janakj)
  * 2003-04-07 URI class added (janakj)
420b6bb0
  */
 
1eb01c66
 /*! \file
  * \brief Parser :: Generic Parameter Parser
  *
  * \ingroup parser
  */
 
420b6bb0
 #include <string.h>
 #include "../str.h"
 #include "../ut.h"
 #include "../dprint.h"
 #include "../trim.h"
 #include "../mem/mem.h"
91804f9b
 #include "../mem/shm_mem.h"
420b6bb0
 #include "parse_param.h"
 
 
2eb780d7
 static inline void parse_event_dialog_class(param_hooks_t* h, param_t* p)
 {
 
 	if (!p->name.s) {
 		LOG(L_ERR, "ERROR: parse_event_dialog_class: empty value\n");
 		return;
 	}
 	if (!h) {
 		LOG(L_CRIT, "BUG: parse_event_dialog_class: NULL param hook pointer\n");
 		return;
 	}
 	switch(p->name.s[0]) {
 	case 'c':
 	case 'C':
 		if ((p->name.len == 7) &&
 		    (!strncasecmp(p->name.s + 1, "all-id", 6))) {
 			p->type = P_CALL_ID;
 			h->event_dialog.call_id = p;
 		}
 		break;
 
 	case 'f':
 	case 'F':
 		if ((p->name.len == 8) &&
 		    (!strncasecmp(p->name.s + 1, "rom-tag", 7))) {
 			p->type = P_FROM_TAG;
 			h->event_dialog.from_tag = p;
 		}
 		break;
 
 	case 't':
 	case 'T':
 		if ((p->name.len == 6) &&
 		    (!strncasecmp(p->name.s + 1, "o-tag", 5))) {
 			p->type = P_TO_TAG;
 			h->event_dialog.to_tag = p;
 		}
 		break;
 
 	case 'i':
 	case 'I':
 		if ((p->name.len == 27) &&
 		    (!strncasecmp(p->name.s + 1, "nclude-session-description", 26))) {
 			p->type = P_ISD;
 			h->event_dialog.include_session_description = p;
 		}
 		break;
 
 	case 's':
 	case 'S':
 		if ((p->name.len == 3) &&
 		    (!strncasecmp(p->name.s + 1, "la", 2))) {
 			p->type = P_SLA;
 			h->event_dialog.sla = p;
 		}
 		break;
3f8b07ca
 
 	case 'm':
 	case 'M':
 		if ((p->name.len == 2) &&
 		    (!strncasecmp(p->name.s + 1, "a", 1))) {
 			p->type = P_MA;
 			h->event_dialog.ma = p;
 		}
 		break;
2eb780d7
 	}
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Try to find out parameter name, recognized parameters
524b4677
  * are q, expires and methods
420b6bb0
  */
 static inline void parse_contact_class(param_hooks_t* _h, param_t* _p)
 {
80a4d8cd
 
 	if (!_p->name.s) {
 		LOG(L_ERR, "ERROR: parse_contact_class: empty value\n");
 		return;
 	}
2cc36bfc
 	if (!_h) {
 		LOG(L_CRIT, "BUG: parse_uri_class: NULL param hook pointer\n");
 		return;
 	}
420b6bb0
 	switch(_p->name.s[0]) {
 	case 'q':
 	case 'Q':
 		if (_p->name.len == 1) {
 			_p->type = P_Q;
 			_h->contact.q = _p;
 		}
 		break;
 		
 	case 'e':
 	case 'E':
 		if ((_p->name.len == 7) &&
 		    (!strncasecmp(_p->name.s + 1, "xpires", 6))) {
 			_p->type = P_EXPIRES;
 			_h->contact.expires = _p;
 		}
 		break;
 		
 	case 'm':
 	case 'M':
524b4677
 		if ((_p->name.len == 7) &&
 		    (!strncasecmp(_p->name.s + 1, "ethods", 6))) {
 			_p->type = P_METHODS;
 			_h->contact.methods = _p;
420b6bb0
 		}
 		break;
3294f904
 		
 	case 'r':
 	case 'R':
 		if ((_p->name.len == 8) &&
 		    (!strncasecmp(_p->name.s + 1, "eceived", 7))) {
 			_p->type = P_RECEIVED;
ffcf5471
 			_h->contact.received = _p;
3294f904
 		}
 		break;
9cda3be9
 	case '+':
 		if ((_p->name.len == 13) &&
 			(!strncasecmp(_p->name.s + 1, "sip.instance", 12))) {
 			_p->type = P_INSTANCE;
 			_h->contact.instance = _p;
 		}
 		break;
420b6bb0
 	}
 }
 
 
1eb01c66
 /*! \brief
ed49bd28
  * Try to find out parameter name, recognized parameters
  * are transport, lr, r2, maddr
  */
 static inline void parse_uri_class(param_hooks_t* _h, param_t* _p)
 {
80a4d8cd
 
 	if (!_p->name.s) {
 		LOG(L_ERR, "ERROR: parse_uri_class: empty value\n");
 		return;
 	}
2cc36bfc
 	if (!_h) {
 		LOG(L_CRIT, "BUG: parse_uri_class: NULL param hook pointer\n");
 		return;
 	}
ed49bd28
 	switch(_p->name.s[0]) {
 	case 't':
 	case 'T':
 		if ((_p->name.len == 9) &&
 		    (!strncasecmp(_p->name.s + 1, "ransport", 8))) {
 			_p->type = P_TRANSPORT;
 			_h->uri.transport = _p;
0b5f5b2e
 		} else if (_p->name.len == 2) {
 			if (((_p->name.s[1] == 't') || (_p->name.s[1] == 'T')) &&
 			    ((_p->name.s[2] == 'l') || (_p->name.s[2] == 'L'))) {
 				_p->type = P_TTL;
 				_h->uri.ttl = _p;
 			}
ed49bd28
 		}
 		break;
 
 	case 'l':
 	case 'L':
 		if ((_p->name.len == 2) && ((_p->name.s[1] == 'r') || (_p->name.s[1] == 'R'))) {
 			_p->type = P_LR;
 			_h->uri.lr = _p;
 		}
 		break;
 
 	case 'r':
 	case 'R':
 		if ((_p->name.len == 2) && (_p->name.s[1] == '2')) {
 			_p->type = P_R2;
 			_h->uri.r2 = _p;
 		}
 		break;
 
 	case 'm':
 	case 'M':
 		if ((_p->name.len == 5) &&
 		    (!strncasecmp(_p->name.s + 1, "addr", 4))) {
 			_p->type = P_MADDR;
e4fbe984
 			_h->uri.maddr = _p;
ed49bd28
 		}
 		break;
eb91ce7f
 		
 	case 'd':
 	case 'D':
 		if ((_p->name.len == 5) &&
 		    (!strncasecmp(_p->name.s + 1, "stip", 4))) {
 			_p->type = P_DSTIP;
 			_h->uri.dstip = _p;
 		} else if ((_p->name.len == 7) &&
 			   (!strncasecmp(_p->name.s + 1, "stport", 6))) {
 			_p->type = P_DSTPORT;
 			_h->uri.dstport = _p;
 		}
 		break;
46b2c44a
 	case 'f':
 	case 'F':
 		if ((_p->name.len == 4) &&
 		    (!strncasecmp(_p->name.s + 1, "tag", 3))) {
 			_p->type = P_FTAG;
 			_h->uri.ftag = _p;
 		}
 		break;
ed49bd28
 	}
eb91ce7f
 
ed49bd28
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse quoted string in a parameter body
  * return the string without quotes in _r
  * parameter and update _s to point behind the
  * closing quote
  */
 static inline int parse_quoted_param(str* _s, str* _r)
 {
 	char* end_quote;
 
 	     /* The string must have at least
 	      * surrounding quotes
 	      */
 	if (_s->len < 2) {
 		return -1;
 	}
 
 	     /* Skip opening quote */
 	_s->s++;
 	_s->len--;
 
 
 	     /* Find closing quote */
 	end_quote = q_memchr(_s->s, '\"', _s->len);
 
 	     /* Not found, return error */
 	if (!end_quote) {
 		return -2;
 	}
 
 	     /* Let _r point to the string without
 	      * surrounding quotes
 	      */
 	_r->s = _s->s;
 	_r->len = end_quote - _s->s;
 
 	     /* Update _s parameter to point
 	      * behind the closing quote
 	      */
 	_s->len -= (end_quote - _s->s + 1);
 	_s->s = end_quote + 1;
 
 	     /* Everything went OK */
 	return 0;
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse unquoted token in a parameter body
  * let _r point to the token and update _s
  * to point right behind the token
  */
 static inline int parse_token_param(str* _s, str* _r)
 {
 	int i;
 
 	     /* There is nothing to parse,
 	      * return error
 	      */
 	if (_s->len == 0) {
 		return -1;
 	}
 
 	     /* Save the begining of the
 	      * token in _r->s
 	      */
 	_r->s = _s->s;
 
c32feee5
 	     /* Iterate through the
420b6bb0
 	      * token body
 	      */
 	for(i = 0; i < _s->len; i++) {
 
 		     /* All these characters
 		      * mark end of the token
 		      */
 		switch(_s->s[i]) {
 		case ' ':
 		case '\t':
 		case '\r':
 		case '\n':
 		case ',':
 		case ';':
 			     /* So if you find
 			      * any of them
 			      * stop iterating
 			      */
 			goto out;
 		}
 	}
  out:
 	if (i == 0) {
 		return -1;
         }
 
 	     /* Save length of the token */
         _r->len = i;
 
 	     /* Update _s parameter so it points
 	      * right behind the end of the token
 	      */
 	_s->s = _s->s + i;
 	_s->len -= i;
 
 	     /* Everything went OK */
 	return 0;
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse a parameter name
  */
 static inline void parse_param_name(str* _s, pclass_t _c, param_hooks_t* _h, param_t* _p)
 {
80a4d8cd
 
 	if (!_s->s) {
 		DBG("DEBUG: parse_param_name: empty parameter\n");
 		return;
 	}
 
420b6bb0
 	_p->name.s = _s->s;
 
 	while(_s->len) {
 		switch(_s->s[0]) {
 		case ' ':
 		case '\t':
 		case '\r':
 		case '\n':
 		case ';':
 		case ',':
 		case '=':
 			goto out;
 		}
 		_s->s++;
 		_s->len--;
 	}
 
  out:
 	_p->name.len = _s->s - _p->name.s;
2cc36bfc
 
420b6bb0
 	switch(_c) {
 	case CLASS_CONTACT: parse_contact_class(_h, _p); break;
ed49bd28
 	case CLASS_URI:     parse_uri_class(_h, _p);     break;
2eb780d7
 	case CLASS_EVENT_DIALOG: parse_event_dialog_class(_h, _p); break;
420b6bb0
 	default: break;
 	}
 }
 
 
4c4e112f
 
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse body of a parameter. It can be quoted string or
  * a single token.
  */
 static inline int parse_param_body(str* _s, param_t* _c)
 {
 	if (_s->s[0] == '\"') {
 		if (parse_quoted_param(_s, &(_c->body)) < 0) {
 			LOG(L_ERR, "parse_param_body(): Error while parsing quoted string\n");
 			return -2;
 		}
 	} else {
 		if (parse_token_param(_s, &(_c->body)) < 0) {
 			LOG(L_ERR, "parse_param_body(): Error while parsing token\n");
 			return -3;
 		}
 	}
 
 	return 0;
 }
 
 
1eb01c66
 /*!  \brief
c91680f4
  * Only parse one parameter
  * Returns:
  * 	t: out parameter
  * 	-1: on error
  * 	0: success, but expect a next paramter
  * 	1: success and exepect no more parameters
  */
 inline int parse_param(str *_s, pclass_t _c, param_hooks_t *_h, param_t *t)
 {
 	memset(t, 0, sizeof(param_t));
 
 	parse_param_name(_s, _c, _h, t);
 	trim_leading(_s);
 	
 	if (_s->len == 0) { /* The last parameter without body */
 		t->len = t->name.len;
 		goto ok;
 	}
 	
 	if (_s->s[0] == '=') {
 		_s->s++;
 		_s->len--;
 		trim_leading(_s);
 
 		if (_s->len == 0) {
a3b2fcde
 		    /* Be forgiving and accept parameters with missing value,
afd800bd
 		     * we just set the length of parameter body to 0. */
 		    t->body.s = _s->s;
a3b2fcde
 		    t->body.len = 0;
 		} else if (parse_param_body(_s, t) < 0) {
c91680f4
 			LOG(L_ERR, "parse_params(): Error while parsing param body\n");
 			goto error;
 		}
 
 		t->len = _s->s - t->name.s;
 
 		trim_leading(_s);
 		if (_s->len == 0) {
 			goto ok;
 		}
 	} else {
 		t->len = t->name.len;
 	}
 
 	if (_s->s[0] == ',') goto ok; /* To be able to parse header parameters */
 	if (_s->s[0] == '>') goto ok; /* To be able to parse URI parameters */
 
 	if (_s->s[0] != ';') {
 		LOG(L_ERR, "parse_params(): Invalid character, ; expected\n");
 		goto error;
 	}
 
 	_s->s++;
 	_s->len--;
 	trim_leading(_s);
 	
 	if (_s->len == 0) {
 		LOG(L_ERR, "parse_params(): Param name missing after ;\n");
 		goto error;
 	}
 
 	return 0; /* expect more params */
 
 ok:
 	return 1; /* done with parsing for params */
 error:
 	return -1;
 }
 
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse parameters
7af69774
  * \param _s is string containing parameters, it will be updated to point behind the parameters
  * \param _c is class of parameters
  * \param _h is pointer to structure that will be filled with pointer to well known parameters
  * \param _p pointing to linked list where parsed parameters will be stored
  * \return 0 on success and negative number on an error
420b6bb0
  */
 int parse_params(str* _s, pclass_t _c, param_hooks_t* _h, param_t** _p)
 {
 	param_t* t;
 
2cc36bfc
 	if (!_s || !_p) {
420b6bb0
 		LOG(L_ERR, "parse_params(): Invalid parameter value\n");
 		return -1;
 	}
 
2cc36bfc
 	if (_h)
 		memset(_h, 0, sizeof(param_hooks_t));
afc00488
 	*_p = 0;
ddda1e10
 
80a4d8cd
 	if (!_s->s) { /* no parameters at all -- we're done */
 		DBG("DEBUG: parse_params: empty uri params, skipping\n");
 		return 0;
 	}
 			
420b6bb0
 	while(1) {
 		t = (param_t*)pkg_malloc(sizeof(param_t));
 		if (t == 0) {
 			LOG(L_ERR, "parse_params(): No memory left\n");
 			goto error;
 		}
 
c91680f4
 		switch(parse_param(_s, _c, _h, t)) {
 		case 0: break;
 		case 1: goto ok;
 		default: goto error;
420b6bb0
 		}
 
 		t->next = *_p;
 		*_p = t;
 	}
 
  error:
 	if (t) pkg_free(t);
 	free_params(*_p);
 	return -2;
 
  ok:
 	t->next = *_p;
 	*_p = t;
 	return 0;
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Free linked list of parameters
  */
91804f9b
 static inline void do_free_params(param_t* _p, int _shm)
420b6bb0
 {
 	param_t* ptr;
 	
 	while(_p) {
 		ptr = _p;
 		_p = _p->next;
91804f9b
 		if (_shm) shm_free(ptr);
 		else pkg_free(ptr);
 	}	
 }
 
 
1eb01c66
 /*! \brief
91804f9b
  * Free linked list of parameters
  */
 void free_params(param_t* _p)
 {
 	do_free_params(_p, 0);
 }
 
 
1eb01c66
 /*! \brief
91804f9b
  * Free linked list of parameters
  */
 void shm_free_params(param_t* _p)
 {
 	do_free_params(_p, 1);
420b6bb0
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Print a parameter structure, just for debugging
  */
1c6135b6
 static inline void print_param(FILE* _o, param_t* _p)
420b6bb0
 {
 	char* type;
 
1c6135b6
 	fprintf(_o, "---param(%p)---\n", _p);
420b6bb0
 	
 	switch(_p->type) {
ed49bd28
 	case P_OTHER:     type = "P_OTHER";     break;
 	case P_Q:         type = "P_Q";         break;
 	case P_EXPIRES:   type = "P_EXPIRES";   break;
524b4677
 	case P_METHODS:   type = "P_METHODS";   break;
ed49bd28
 	case P_TRANSPORT: type = "P_TRANSPORT"; break;
 	case P_LR:        type = "P_LR";        break;
 	case P_R2:        type = "P_R2";        break;
 	case P_MADDR:     type = "P_MADDR";     break;
0b5f5b2e
 	case P_TTL:       type = "P_TTL";       break;
3294f904
 	case P_RECEIVED:  type = "P_RECEIVED";  break;
eb91ce7f
 	case P_DSTIP:     type = "P_DSTIP";     break;
 	case P_DSTPORT:   type = "P_DSTPORT";   break;
9cda3be9
 	case P_INSTANCE:  type = "P_INSTANCE";  break;
46b2c44a
 	case P_FTAG:      type = "P_FTAG";      break;
2eb780d7
 	case P_CALL_ID:   type = "P_CALL_ID";   break;
 	case P_FROM_TAG:  type = "P_FROM_TAG";  break;
 	case P_TO_TAG:    type = "P_TO_TAG";    break;
 	case P_ISD:       type = "P_ISD";       break;
 	case P_SLA:       type = "P_SLA";       break;
ed49bd28
 	default:          type = "UNKNOWN";     break;
420b6bb0
 	}
 	
1c6135b6
 	fprintf(_o, "type: %s\n", type);
 	fprintf(_o, "name: \'%.*s\'\n", _p->name.len, _p->name.s);
 	fprintf(_o, "body: \'%.*s\'\n", _p->body.len, _p->body.s);
 	fprintf(_o, "len : %d\n", _p->len);
 	fprintf(_o, "---/param---\n");
420b6bb0
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Print linked list of parameters, just for debugging
  */
1c6135b6
 void print_params(FILE* _o, param_t* _p)
420b6bb0
 {
 	param_t* ptr;
 	
 	ptr = _p;
 	while(ptr) {
1c6135b6
 		print_param(_o, ptr);
420b6bb0
 		ptr = ptr->next;
 	}
 }
5d17eb17
 
 
1eb01c66
 /*! \brief
5d17eb17
  * Duplicate linked list of parameters
  */
91804f9b
 static inline int do_duplicate_params(param_t** _n, param_t* _p, int _shm)
5d17eb17
 {
 	param_t* last, *ptr, *t;
 
bfcd8364
 	if (!_n) {
5d17eb17
 		LOG(L_ERR, "duplicate_params(): Invalid parameter value\n");
 		return -1;
 	}
 	
 	last = 0;
 	*_n = 0;
 	ptr = _p;
 	while(ptr) {
91804f9b
 		if (_shm) {
 			t = (param_t*)shm_malloc(sizeof(param_t));
 		} else {
 			t = (param_t*)pkg_malloc(sizeof(param_t));
 		}
5d17eb17
 		if (!t) {
 			LOG(L_ERR, "duplicate_params(): Invalid parameter value\n");
 			goto err;
 		}
 		memcpy(t, ptr, sizeof(param_t));
 		t->next = 0;
 
 		if (!*_n) *_n = t;
 		if (last) last->next = t;
 		last = t;
 
 		ptr = ptr->next;
 	}
bfcd8364
 	return 0;
5d17eb17
 
  err:
91804f9b
 	do_free_params(*_n, _shm);
bfcd8364
 	return -2;
5d17eb17
 }
91804f9b
 
 
1eb01c66
 /*! \brief
91804f9b
  * Duplicate linked list of parameters
  */
 int duplicate_params(param_t** _n, param_t* _p)
 {
 	return do_duplicate_params(_n, _p, 0);
 }
 
 
1eb01c66
 /*! \brief
91804f9b
  * Duplicate linked list of parameters
  */
 int shm_duplicate_params(param_t** _n, param_t* _p)
 {
 	return do_duplicate_params(_n, _p, 1);
 }