select.c
a0fb4e8b
 /*
  * $Id$
  *
  * Copyright (C) 2005-2006 iptelorg GmbH
  *
  * 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.
  *
2dd048ea
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
a0fb4e8b
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * History:
  * --------
  *  2005-12-19  select framework (mma)
43f88e4f
  *  2006-01-19  multiple nested calls, IS_ALIAS -> NESTED flag renamed (mma)
  *              DIVERSION flag checked
5c11480e
  *  2006-02-26  don't free str when changing type STR -> DIVERSION (mma)
  *				it can't be freeable sometimes (e.g. xlog's select)
6f1faf76
  *	2006-05-30  parse_select (mma)
  *	2006-06-02  shm_parse_select (mma)
5c11480e
  *
a0fb4e8b
  */
 
6f1faf76
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <ctype.h>
a0fb4e8b
 
3fb428ed
 #include "select.h"
 #include "dprint.h"
 #include "select_core.h"
 #include "mem/mem.h"
6f1faf76
 #include "mem/shm_mem.h"
3fb428ed
 
 /*
  * The main parser table list placeholder
  * at startup use core table, modules can
  * add their own via register_select_table call
  */
 static select_table_t *select_list = &select_core_table;
 
559cb457
 /* the level of the select call that is beeing evaluated
  * by the child process
  */
 int select_level = 0;
 
8321667b
 /* pointer to the SIP uri beeing processed.
  * Nested function calls can pass information to each
  * other using this pointer. Only for performace reasons.
  * (Miklos)
  */
 struct sip_uri	*select_uri_p = NULL;
 
d22deb6c
 /** parse a select identifier (internal version)
6f1faf76
  * Parse select string into select structure s
d22deb6c
  * moves pointer p to the first unused char.
  *
  * The select identifier must be of the form:
  *   [@] <sel_id> [ '.' <sel_id> ...]
  *   
  * Where 
  *       <sel_id> = <id> |
  *                  <id> '[' <idx> ']'
  *       <id> = [a-zA-Z0-9_]+
  *       <idx> = <number> | <string>
  *       <string> = '"' <ascii> '"' | 
  *                  '\"' <ascii> '\"'
  *
  * Examples:
  *     @to.tag
  *     @hf_value["contact"]
  *     @msg.header["SER-Server-ID"]
  *     @eval.pop[-1]
  *     contact.uri.params.maddr
  *     cfg_get.rtp_proxy.enabled 
6f1faf76
  *
d22deb6c
  * @return -1 error
6f1faf76
  *			  p points to the first unconsumed char
  *          0 success
  *			  p points to the first unconsumed char
  *			  s points to the select structure
  */
 
 int w_parse_select(char**p, select_t* sel)
 {
 	str name;
8aa67e4d
 	char* select_name;
6f1faf76
 	
 	if (**p=='@') (*p)++;
8aa67e4d
 	select_name=*p;
6f1faf76
 	sel->n=0;
f8f678c3
 	while (isalpha((unsigned char)*(*p))) {
6f1faf76
 		if (sel->n > MAX_SELECT_PARAMS -2) {
 			ERR("parse_select: select depth exceeds max\n");
 			goto error;
 		}
 		name.s=(*p);
f8f678c3
 		while (isalpha((unsigned char)*(*p)) || 
 				isdigit((unsigned char)*(*p)) || (*(*p)=='_')) (*p)++;
6f1faf76
 		name.len=(*p)-name.s;
 		sel->params[sel->n].type=SEL_PARAM_STR;
 		sel->params[sel->n].v.s=name;
 		DBG("parse_select: part %d: %.*s\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s);
 		sel->n++;
 		if (*(*p)=='[') {
 			(*p)++; 
b3d26ead
 			if (*(*p)=='\\') (*p)++;
1ee35cdc
 			if (*(*p)=='"') {
 				(*p)++;	
 				name.s=(*p);
b3d26ead
 				while ((*(*p)!='\0') && (*(*p)!='"')) (*p)++;
 				if (*(*p)!='"') {
 					ERR("parse_select: end of string is missing\n");
 					goto error;
 				}
1ee35cdc
 				name.len=(*p)-name.s;
b3d26ead
 				if (*((*p)-1)=='\\') name.len--;
1ee35cdc
 				(*p)++;
 				if (*(*p)!=']') {
 					ERR("parse_select: invalid string index, no closing ]\n");
 					goto error;
 				};
 				(*p)++;
 				sel->params[sel->n].type=SEL_PARAM_STR;
 				sel->params[sel->n].v.s=name;
 				DBG("parse_select: part %d: [\"%.*s\"]\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s);
 			} else {
 				name.s=(*p);
 				if (*(*p)=='-') (*p)++;
f8f678c3
 				while (isdigit((unsigned char)*(*p))) (*p)++;
1ee35cdc
 				name.len=(*p)-name.s;
 				if (*(*p)!=']') {
 					ERR("parse_select: invalid index, no closing ]\n");
 					goto error;
 				};
 				(*p)++;
 				sel->params[sel->n].type=SEL_PARAM_INT;
 				sel->params[sel->n].v.i=atoi(name.s);
 				DBG("parse_select: part %d: [%d]\n", sel->n, sel->params[sel->n].v.i);
 			}
6f1faf76
 			sel->n++;
 		}
 		if (*(*p)!='.') break;
 		(*p)++;
 	};
 	if (sel->n==0) {
03d7d3b9
 		ERR("parse_select: invalid select '%.*s'\n", (int)(*p - select_name), select_name);
6f1faf76
 		goto error;
 	};
 	DBG("parse_select: end, total elements: %d, calling resolve_select\n", sel->n);
 	if (resolve_select(sel)<0) {
03d7d3b9
 		ERR("parse_select: error while resolve_select '%.*s'\n", (int)(*p - select_name), select_name);
6f1faf76
 		goto error;
 	}
 	return 0;
 	
 error:
 	return -1;
 }
 
d22deb6c
 
 /** parse a select identifier.
  * Parse select string into select structure s and
  * moves pointer p to the first unused char.
  * 
  * The select identifier must be of the form:
  *   [@] <sel_id> [ '.' <sel_id> ...]
  *   
  * Where 
  *       <sel_id> = <id> |
  *                  <id> '[' <idx> ']'
  *       <id> = [a-zA-Z0-9_]+
  *       <idx> = <number>  | '-' <number> | <string>
  *       <string> = '"' <ascii> '"' | 
  *                  '\"' <ascii> '\"'
  *
  * Examples:
  *     @to.tag
  *     @hf_value["contact"]
  *     @msg.header["SER-Server-ID"]
  *     @eval.pop[-1]
  *     contact.uri.params.maddr
  *     cfg_get.rtp_proxy.enabled 
   *
   * @param p - double string (asciiz) pointer, *p is moved to the first char
   *            after the select identifier
   * @param s - the result will be stored here
   * @return  < 0 on error, 0 on success
   */
6f1faf76
 int parse_select (char** p, select_t** s)
 {
 	select_t* sel;
 	
 	sel=(select_t*)pkg_malloc(sizeof(select_t));
 	if (!sel) {
 		ERR("parse_select: no free memory\n");
 		return -1;
 	}
 	if (w_parse_select(p, sel)<0) {
f5b61aa0
 		pkg_free(sel);
6f1faf76
 		return -2;
 	}
 	*s=sel;
 	return 0;
 }
 
2a062f98
 void free_select(select_t *s)
 {
 	if (s)
 		pkg_free(s);
 }
 
6f1faf76
 int shm_parse_select (char** p, select_t** s)
 {
 	select_t* sel;
 	
 	sel=(select_t*)shm_malloc(sizeof(select_t));
 	if (!sel) {
 		ERR("parse_select: no free shared memory\n");
 		return -1;
 	}
 	if (w_parse_select(p, sel)<0) {
f5b61aa0
 		shm_free(sel);
6f1faf76
 		return -2;
 	}
 	*s=sel;
 	return 0;
 }
 
3fb428ed
 int resolve_select(select_t* s)
 {
43f88e4f
 	select_f f;
 	int nested;
3fb428ed
 	int param_idx = 0;
 	int table_idx = 0;
f8f678c3
 	select_table_t* t = NULL;
3fb428ed
 	int accept = 0;
2dd048ea
 
43f88e4f
 	f = NULL;
 	nested = 0;
97aa4411
 	memset (s->f, 0, sizeof(s->f));
3fb428ed
 	while (param_idx<s->n) {
 		accept = 0;
b9e4bf3f
 		switch (s->params[param_idx].type) {
 		case SEL_PARAM_STR:
 			DBG("resolve_select: '%.*s'\n", s->params[param_idx].v.s.len, s->params[param_idx].v.s.s);
 			break;
 		case SEL_PARAM_INT:
 			DBG("resolve_select: [%d]\n", s->params[param_idx].v.i);
 			break;
8446b4e0
 		default:
5823b2f7
 			/* just to avoid the warning */
 			break;
b9e4bf3f
 		}
3fb428ed
 		for (t=select_list; t; t=t->next) {
2dd048ea
 			table_idx = 0;
3fb428ed
 			if (!t->table) continue;
 			while (t->table[table_idx].curr_f || t->table[table_idx].new_f) {
 				if (t->table[table_idx].curr_f == f) {
d0cf3d0b
 					if ((t->table[table_idx].flags & (NESTED | CONSUME_NEXT_INT | CONSUME_NEXT_STR)) == NESTED) {
43f88e4f
 						accept = 1;
 					} else if (t->table[table_idx].type == s->params[param_idx].type) {
3fb428ed
 						switch (t->table[table_idx].type) {
ec2b7f55
 						case SEL_PARAM_INT:
3fb428ed
 							accept = 1;
 							break;
43f88e4f
 						case SEL_PARAM_STR:
3fb428ed
 							accept = (((t->table[table_idx].name.len == s->params[param_idx].v.s.len) || !t->table[table_idx].name.len)
 								   && (!t->table[table_idx].name.s || !strncasecmp(t->table[table_idx].name.s, s->params[param_idx].v.s.s, s->params[param_idx].v.s.len)));
 							break;
 						default:
 							break;
 						}
 					};
 				}
 				if (accept) goto accepted;
 				table_idx++;
 			}
 		}
2dd048ea
 		switch (s->params[param_idx].type) {
 			case SEL_PARAM_STR:
 				LOG(L_ERR, "Unable to resolve select '%.*s' at level %d\n", s->params[param_idx].v.s.len, s->params[param_idx].v.s.s, param_idx);
 				break;
 			case SEL_PARAM_INT:
 				LOG(L_ERR, "Unable to resolve select [%d] at level %d\n", s->params[param_idx].v.i, param_idx);
 				break;
 			default:
 				BUG ("Unable to resolve select at level %d\n", param_idx);
 				break;
 			break;
 		}
3fb428ed
 		goto not_found;
 
 		accepted:
43f88e4f
 		if (t->table[table_idx].flags & DIVERSION) {
5c11480e
 			/* if (s->params[param_idx].type == SEL_PARAM_STR) pkg_free(s->params[param_idx].v.s.s); */
 			/* don't free it (the mem can leak only once at startup)
 			 * the parsed string can live inside larger string block
 			 * e.g. when xlog's select is parsed
 			 */
43f88e4f
 			s->params[param_idx].type = SEL_PARAM_DIV;
 			s->params[param_idx].v.i = t->table[table_idx].flags & DIVERSION_MASK;
2dd048ea
 
43f88e4f
 		}
3fb428ed
 		if (t->table[table_idx].flags & CONSUME_NEXT_STR) {
ec2b7f55
 			if ((param_idx<s->n-1) && (s->params[param_idx+1].type == SEL_PARAM_STR)) {
3fb428ed
 				param_idx++;
 			} else if (!(t->table[table_idx].flags & OPTIONAL)) {
43f88e4f
 				BUG ("Mandatory STR parameter not found\n");
3fb428ed
 				goto not_found;
 			}
 		}
 		if (t->table[table_idx].flags & CONSUME_NEXT_INT) {
ec2b7f55
 			if ((param_idx<s->n-1) && (s->params[param_idx+1].type == SEL_PARAM_INT)) {
3fb428ed
 				param_idx++;
 			} else if (!(t->table[table_idx].flags & OPTIONAL)) {
43f88e4f
 				BUG ("Mandatory INT parameter not found\n");
3fb428ed
 				goto not_found;
 			}
 		}
2dd048ea
 
43f88e4f
 		if (t->table[table_idx].flags & NESTED) {
 			if (nested < MAX_NESTED_CALLS-1) { /* need space for final function */
 				s->f[nested++] = f;
84b87bd3
 				s->param_offset[nested] = param_idx;
43f88e4f
 			} else {
 				BUG("MAX_NESTED_CALLS too small to resolve select\n");
 				goto not_found;
 			}
3fb428ed
 		} else {
 			param_idx++;
 		}
6fcb46be
 
 		if (t->table[table_idx].flags & FIXUP_CALL) {
559cb457
 			select_level = nested;
1ee35cdc
 			s->param_offset[nested+1] = param_idx;
6fcb46be
 			if (t->table[table_idx].new_f(NULL, s, NULL)<0) goto not_found;
 		}
 
3fb428ed
 		f = t->table[table_idx].new_f;
c935a4a3
 
 		if (t->table[table_idx].flags & CONSUME_ALL) {
 			/* sanity checks */
 			if (t->table[table_idx].flags & NESTED)
 				WARN("resolve_select: CONSUME_ALL should not be set "
 					"together with NESTED flag!\n");
 			if ((t->table[table_idx].flags & FIXUP_CALL) == 0)
 				WARN("resolve_select: FIXUP_CALL should be defined "
 					"if CONSUME_ALL flag is set!\n");
 			break;
 		}
3fb428ed
 	}
 
43f88e4f
 	if (t->table[table_idx].flags & SEL_PARAM_EXPECTED) {
 		BUG ("final node has SEL_PARAM_EXPECTED set (no more parameters available)\n");
 		goto not_found;
 	}
 	if (nested >= MAX_NESTED_CALLS) {
 		BUG("MAX_NESTED_CALLS too small, no space for finally resolved function\n");
 		goto not_found;
 	}
 	if ((nested>0) && (s->f[nested-1] == f)) {
 		BUG("Topmost nested function equals to final function, won't call it twice\n");
 	} else {
8c5e4afe
 		s->f[nested++] = f;
43f88e4f
 	}
8c5e4afe
 	s->param_offset[nested] = s->n;
2dd048ea
 
3fb428ed
 	return 0;
2dd048ea
 
3fb428ed
 not_found:
 	return -1;
 }
 
 int run_select(str* res, select_t* s, struct sip_msg* msg)
 {
559cb457
 	int ret, orig_level;
2dd048ea
 
3fb428ed
 	if (res == NULL) {
 		BUG("Select unprepared result space\n");
 		return -1;
 	}
 	if (s == 0) {
 		BUG("Select structure is NULL\n");
 		return -1;
 	}
43f88e4f
 	if (s->f[0] == 0) {
3fb428ed
 		BUG("Select structure has not been resolved\n");
 		return -1;
 	}
43f88e4f
 	DBG("Calling SELECT %p \n", s->f);
 
8321667b
 	/* reset the uri pointer */
 	select_uri_p = NULL;
 
34873f53
 	/* save and restore the original select_level
 	 * because of the nested selects */
559cb457
 	orig_level = select_level;
43f88e4f
 	ret = 0;
559cb457
 	for (	select_level=0;
 		(ret == 0) && (s->f[select_level] !=0 ) && (select_level<MAX_NESTED_CALLS);
 		select_level++
 	) {
 		ret = s->f[select_level](res, s, msg);
43f88e4f
 	}
559cb457
 	select_level = orig_level;
43f88e4f
 	return ret;
3fb428ed
 }
 
 void print_select(select_t* s)
 {
 	int i;
 	DBG("select(");
 	for(i = 0; i < s->n; i++) {
ec2b7f55
 		if (s->params[i].type == SEL_PARAM_INT) {
3fb428ed
 			DBG("%d,", s->params[i].v.i);
 		} else {
 			DBG("%.*s,", s->params[i].v.s.len, s->params[i].v.s.s);
 		}
 	}
 	DBG(")\n");
 }
 
 int register_select_table(select_row_t* mod_tab)
 {
 	select_table_t* t;
 	t=(select_table_t*)pkg_malloc(sizeof(select_table_t));
 	if (!t) {
 		ERR("No memory for new select_table structure\n");
 		return -1;
 	}
2dd048ea
 
3fb428ed
 	t->table=mod_tab;
 	t->next=select_list;
 	select_list=t;
 	return 0;
 }