Browse Source

modules/mtree and modules_k/textops: new stuff for release after 3.2
- mtree enhancements
- new in_list() textops function

Juha Heinanen 14 years ago
parent
commit
1d5937377b

+ 29 - 15
modules/mtree/README

@@ -49,6 +49,7 @@ Juha Heinanen
               3.10. pv_values (string)
               3.11. mt_tree_type (integer)
               3.12. mt_ignore_duplicates (integer)
+              3.13. mt_allow_duplicates (integer)
 
         4. Exported Functions
 
@@ -74,7 +75,8 @@ Juha Heinanen
    1.10. Set pv_values parameter
    1.11. Set mt_tree_type parameter
    1.12. Set mt_ignore_duplicates parameter
-   1.13. mt_match usage
+   1.13. Set mt_allow_duplicates parameter
+   1.14. mt_match usage
 
 Chapter 1. Admin Guide
 
@@ -100,6 +102,7 @@ Chapter 1. Admin Guide
         3.10. pv_values (string)
         3.11. mt_tree_type (integer)
         3.12. mt_ignore_duplicates (integer)
+        3.13. mt_allow_duplicates (integer)
 
    4. Exported Functions
 
@@ -146,6 +149,7 @@ Chapter 1. Admin Guide
    3.10. pv_values (string)
    3.11. mt_tree_type (integer)
    3.12. mt_ignore_duplicates (integer)
+   3.13. mt_allow_duplicates (integer)
 
 3.1. db_url (string)
 
@@ -250,9 +254,8 @@ modparam("mtree", "pv_value", "$var(mtval)")
 
 3.10. pv_values (string)
 
-   The PV spec where to store the matched values when mtree consists of
-   string values and mode of mt_match() call has value 2. It can be any
-   AVP.
+   The PV spec where to store the matched values when mtree is of type 0
+   or 2 and mode of mt_match() call has value 2. It can be any AVP.
 
    Default value is “$avp(s:tvalues)”.
 
@@ -263,13 +266,14 @@ modparam("mtree", "pv_values", "$avp(mtvals)")
 
 3.11. mt_tree_type (integer)
 
-   Default payload type for trees data stored in 'db_table'.
+   Default payload type for trees data stored in 'db_table'. Documented
+   values are 0 for string payloads and 2 for integer payloads.
 
    Default value is 0.
 
    Example 1.11. Set mt_tree_type parameter
 ...
-modparam("mtree", "mt_tree_type", 1)
+modparam("mtree", "mt_tree_type", 2)
 ...
 
 3.12. mt_ignore_duplicates (integer)
@@ -283,22 +287,32 @@ modparam("mtree", "mt_tree_type", 1)
 modparam("mtree", "mt_ignore_duplicates", 1)
 ...
 
+3.13. mt_allow_duplicates (integer)
+
+   Allow duplicate prefixes when loading data.
+
+   Default value is 0.
+
+   Example 1.13. Set mt_allow_duplicates parameter
+...
+modparam("mtree", "mt_allow_duplicates", 1)
+...
+
 4. Exported Functions
 
    4.1. mt_match(mtree, pv, mode)
 
 4.1.  mt_match(mtree, pv, mode)
 
-   Match 'pv' value against 'mtree'. If 'mtree' consists of string values
-   (mtree type = 0) and value of 'mode' is NOT 2, sets value of longest
-   matching prefix to pseudo variable specified by pv_value parameter. If
-   'mtree' consists of string values (mtree type = 0) and value of 'mode'
-   is 2, sets values of all matching prefixes to avp specified by
-   pv_values parameter so that value of longest matching prefix is in avp
-   index 0. 'mode' can be an integer constant or a pseudo variable with
-   integer value.
+   Match 'pv' value against 'mtree'. If 'mtree' type is 0 or 2 and value
+   of 'mode' is NOT 2, sets a value of longest matching prefix to pseudo
+   variable specified by pv_value parameter. If 'mtree' type is 0 or 2 and
+   value of 'mode' is 2, sets values of all matching prefixes to avp
+   specified by pv_values parameter so that a value of longest matching
+   prefix is in avp index 0. Parameter 'mode' can be an integer constant
+   or a pseudo variable with integer value.
 
-   Example 1.13. mt_match usage
+   Example 1.14. mt_match usage
 ...
 mt_match("mytree", "$rU", "0");
 ...

+ 24 - 4
modules/mtree/doc/mtree_admin.xml

@@ -238,7 +238,7 @@ modparam("mtree", "pv_value", "$var(mtval)")
 	<section>
 	    <title><varname>pv_values</varname> (string)</title>
 	    <para>
-		The PV spec where to store the matched values when mtree consists of string values and mode of mt_match() call has value 2. It can be any AVP.
+		The PV spec where to store the matched values when mtree is of type 0 or 2 and mode of mt_match() call has value 2. It can be any AVP.
 		</para>
 	    <para>
 		<emphasis>
@@ -258,7 +258,7 @@ modparam("mtree", "pv_values", "$avp(mtvals)")
 	<section>
 	    <title><varname>mt_tree_type</varname> (integer)</title>
 	    <para>
-		Default payload type for trees data stored in 'db_table'.
+		Default payload type for trees data stored in 'db_table'.  Documented values are 0 for string payloads and 2 for integer payloads.
 	    </para>
 	    <para>
 		<emphasis>
@@ -269,7 +269,7 @@ modparam("mtree", "pv_values", "$avp(mtvals)")
 		<title>Set <varname>mt_tree_type</varname> parameter</title>
 		<programlisting format="linespecific">
 ...
-modparam("mtree", "mt_tree_type", 1)
+modparam("mtree", "mt_tree_type", 2)
 ...
 </programlisting>
 	    </example>
@@ -295,6 +295,26 @@ modparam("mtree", "mt_ignore_duplicates", 1)
 	    </example>
 	</section>
 
+	<section>
+	    <title><varname>mt_allow_duplicates</varname> (integer)</title>
+	    <para>
+		Allow duplicate prefixes when loading data.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is 0.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>mt_allow_duplicates</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("mtree", "mt_allow_duplicates", 1)
+...
+</programlisting>
+	    </example>
+	</section>
+
 	</section>
 	
     <section>
@@ -304,7 +324,7 @@ modparam("mtree", "mt_ignore_duplicates", 1)
 		<function moreinfo="none">mt_match(mtree, pv, mode)</function>
 	    </title>
 	    <para>
-		Match 'pv' value against 'mtree'. If 'mtree' consists of string values (mtree type = 0) and value of 'mode' is NOT 2, sets value of longest matching prefix to pseudo variable specified by pv_value parameter. If 'mtree' consists of string values (mtree type = 0) and value of 'mode' is 2, sets values of all matching prefixes to avp specified by pv_values parameter so that value of longest matching prefix is in avp index 0.  'mode' can be an integer constant or a pseudo variable with integer value.
+		Match 'pv' value against 'mtree'. If 'mtree' type is 0 or 2 and value of 'mode' is NOT 2, sets a value of longest matching prefix to pseudo variable specified by pv_value parameter. If 'mtree' type is 0 or 2 and value of 'mode' is 2, sets values of all matching prefixes to avp specified by pv_values parameter so that a value of longest matching prefix is in avp index 0.  Parameter 'mode' can be an integer constant or a pseudo variable with integer value.
 	    </para>
 		<example>
 		<title><function>mt_match</function> usage</title>

+ 130 - 88
modules/mtree/mtree.c

@@ -45,6 +45,7 @@ extern pv_spec_t pv_dstid;
 extern pv_spec_t pv_weight;
 extern int _mt_tree_type;
 extern int _mt_ignore_duplicates;
+extern int _mt_allow_duplicates;
 
 /** structures containing prefix-value pairs */
 static m_tree_t **_ptree = NULL; 
@@ -136,13 +137,14 @@ m_tree_t* mt_init_tree(str* tname, str *dbtable, int type)
 	return pt;
 }
 
-int mt_add_to_tree(m_tree_t *pt, str *sp, str *sv)
+int mt_add_to_tree(m_tree_t *pt, str *sp, str *svalue)
 {
-	int l;
+        int l, ivalue = 0;
 	mt_node_t *itn, *itn0;
+	mt_is_t *tvalues;
 	
 	if(pt==NULL || sp==NULL || sp->s==NULL
-			|| sv==NULL || sv->s==NULL)
+			|| svalue==NULL || svalue->s==NULL)
 	{
 		LM_ERR("bad parameters\n");
 		return -1;
@@ -153,7 +155,12 @@ int mt_add_to_tree(m_tree_t *pt, str *sp, str *sv)
 		LM_ERR("max prefix len exceeded\n");
 		return -1;
 	}
-	
+
+	if ((pt->type == MT_TREE_IVAL) && (str2sint(svalue, &ivalue) != 0)) {
+	    LM_ERR("bad integer string <%.*s>\n", svalue->len, svalue->s);
+	    return -1;
+	}
+
 	l = 0;
 	if(pt->head == NULL)
 	{
@@ -203,33 +210,44 @@ int mt_add_to_tree(m_tree_t *pt, str *sp, str *sv)
 		itn = itn0[_mt_char_table[(unsigned int)sp->s[l]]].child;
 	}
 
-	if(itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s!=NULL)
-	{
-		LM_ERR("prefix already allocated [%.*s/%.*s] old: %.*s\n",
-			sp->len, sp->s, sv->len, sv->s,
-			itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.len,
-			itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s);
-		if(_mt_ignore_duplicates!=0)
-			return 1;
+	if(itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalues != NULL) {
+	    if(_mt_ignore_duplicates != 0) {
+		LM_NOTICE("prefix already allocated [%.*s/%.*s]\n",
+			  sp->len, sp->s, svalue->len, svalue->s);
+		return 1;
+	    } else if (_mt_allow_duplicates == 0) {
+		LM_ERR("prefix already allocated [%.*s/%.*s]\n",
+		       sp->len, sp->s, svalue->len, svalue->s);
 		return -1;
+	    }
 	}
 
-	itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s
-			= (char*)shm_malloc((sv->len+1)*sizeof(char));
-	if(itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s==NULL)
-	{
-		LM_ERR("no more shm mem!\n");
+	tvalues = (mt_is_t *)shm_malloc(sizeof(mt_is_t));
+	if (tvalues == NULL) {
+	    LM_ERR("no more shm mem for tvalue\n");
+	    return -1;
+	}
+	memset(tvalues, 0, sizeof(mt_is_t));
+
+	if (pt->type == MT_TREE_IVAL) {
+	    tvalues->tvalue.n = ivalue;
+	} else { /* pt->type == MT_TREE_SVAL or MT_TREE_DW */
+	    tvalues->tvalue.s.s = (char*)shm_malloc((svalue->len+1)*sizeof(char));
+	    if (tvalues->tvalue.s.s == NULL) {
+		LM_ERR("no more shm mem for string\n");
 		return -1;
+	    }
+	    tvalues->tvalue.s.len = svalue->len;
+	    pt->memsize +=  (svalue->len+1)*sizeof(char);
+	    pt->nritems++;
+	    strncpy(tvalues->tvalue.s.s, svalue->s, svalue->len);
+	    tvalues->tvalue.s.s[svalue->len] = '\0';
 	}
-	pt->memsize +=  (sv->len+1)*sizeof(char);
-	pt->nritems++;
-	strncpy(itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s, sv->s,
-			sv->len);
-	itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.len = sv->len;
-	itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s[sv->len] = '\0';
+	tvalues->next = itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalues;
+	itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalues = tvalues;
+
 	mt_node_set_payload(&itn0[_mt_char_table[(unsigned int)sp->s[l]]],
-			pt->type);
-	
+			    pt->type);
 	return 0;
 }
 
@@ -270,11 +288,11 @@ m_tree_t* mt_get_first_tree()
 }
 
 
-str* mt_get_tvalue(m_tree_t *pt, str *tomatch, int *plen)
+is_t* mt_get_tvalue(m_tree_t *pt, str *tomatch)
 {
-	int l, len;
+	int l;
 	mt_node_t *itn;
-	str *tvalue;
+	is_t *tvalue;
 
 	if(pt==NULL || tomatch==NULL || tomatch->s==NULL)
 	{
@@ -282,7 +300,7 @@ str* mt_get_tvalue(m_tree_t *pt, str *tomatch, int *plen)
 		return NULL;
 	}
 	
-	l = len = 0;
+	l = 0;
 	itn = pt->head;
 	tvalue = NULL;
 
@@ -296,19 +314,15 @@ str* mt_get_tvalue(m_tree_t *pt, str *tomatch, int *plen)
 			return NULL;
 		}
 
-		if(itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalue.s!=NULL)
+		if(itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalues!=NULL)
 		{
-			tvalue = &itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalue;
-			len = l+1;
+			tvalue = &itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalues->tvalue;
 		}
 		
 		itn = itn[_mt_char_table[(unsigned int)tomatch->s[l]]].child;
 		l++;	
 	}
 	
-	if(plen!=NULL)
-		*plen = len;
-	
 	return tvalue;
 }
 
@@ -318,6 +332,7 @@ int mt_add_tvalues(struct sip_msg *msg, m_tree_t *pt, str *tomatch)
     mt_node_t *itn;
     int_str val, values_avp_name;
     unsigned short values_name_type;
+    mt_is_t *tvalues;
 
     if (pt == NULL || tomatch == NULL || tomatch->s == NULL) {
 	LM_ERR("bad parameters\n");
@@ -340,13 +355,21 @@ int mt_add_tvalues(struct sip_msg *msg, m_tree_t *pt, str *tomatch)
 		   l, tomatch->len, tomatch->s);
 	    return -1;
 	}
-
-	if (itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalue.s != NULL) {
-	    val.s = itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalue;
-	    LM_DBG("adding avp <%.*s> with value <%.*s>\n",
-		   values_avp_name.s.len, values_avp_name.s.s,
-		   val.s.len, val.s.s);
-	    add_avp(values_name_type|AVP_VAL_STR, values_avp_name, val);
+	tvalues = itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalues;
+	while (tvalues != NULL) {
+	    if (pt->type == MT_TREE_IVAL) {
+		val.n = tvalues->tvalue.n;
+		LM_DBG("adding avp <%.*s> with value <i:%d>\n",
+		       values_avp_name.s.len, values_avp_name.s.s, val.n);
+		add_avp(values_name_type, values_avp_name, val);
+	    } else {  /* pt->type == MT_TREE_SVAL */
+		val.s = tvalues->tvalue.s;
+		LM_DBG("adding avp <%.*s> with value <s:%.*s>\n",
+		       values_avp_name.s.len, values_avp_name.s.s, val.s.len,
+		       val.s.s);
+		add_avp(values_name_type|AVP_VAL_STR, values_avp_name, val);
+	    }
+	    tvalues = tvalues->next;
 	}
 		
 	itn = itn[_mt_char_table[(unsigned int)tomatch->s[l]]].child;
@@ -363,7 +386,7 @@ int mt_match_prefix(struct sip_msg *msg, m_tree_t *it,
 	int i, j;
 	mt_node_t *itn;
 	int ret;
-	str *tvalue;
+	is_t *tvalue;
 	int_str dstid_avp_name;
 	unsigned short dstid_name_type;
 	int_str weight_avp_name;
@@ -386,26 +409,31 @@ int mt_match_prefix(struct sip_msg *msg, m_tree_t *it,
 
 	l = len = 0;
 	n = 0;
-	if(it->type==MT_TREE_SVAL)
-	{
-	        if (mode == 2) 
-		    return mt_add_tvalues(msg, it, tomatch);
-
-		tvalue = mt_get_tvalue(it, tomatch, &len);
-		if(tvalue==NULL)
-		{
-			LM_DBG("no match for: %.*s\n", tomatch->len, tomatch->s);
-			return -1;
-		}
-		memset(&val, 0, sizeof(pv_value_t));
+	if ((it->type==MT_TREE_SVAL) || (it->type==MT_TREE_IVAL)) {
+	    if (mode == 2) 
+		return mt_add_tvalues(msg, it, tomatch);
+	    tvalue = mt_get_tvalue(it, tomatch);
+	    if (tvalue == NULL) {
+		LM_DBG("no match for: %.*s\n", tomatch->len, tomatch->s);
+		return -1;
+	    }
+	    memset(&val, 0, sizeof(pv_value_t));
+	    if (it->type==MT_TREE_SVAL) {
 		val.flags = PV_VAL_STR;
-		val.rs = *tvalue;
-		if(pv_value.setf(msg, &pv_value.pvp, (int)EQ_T, &val)<0)
-		{
-			LM_ERR("setting PV failed\n");
-			return -2;
+		val.rs = tvalue->s;
+		if(pv_value.setf(msg, &pv_value.pvp, (int)EQ_T, &val)<0) {
+		    LM_ERR("setting PV failed\n");
+		    return -2;
 		}
-		return 0;
+	    } else {
+		val.flags = PV_VAL_INT;
+		val.ri = tvalue->n;
+		if(pv_value.setf(msg, &pv_value.pvp, (int)EQ_T, &val)<0) {
+		    LM_ERR("setting PV failed\n");
+		    return -2;
+		}
+	    }
+	    return 0;
 	}
 
 	if(it->type!=MT_TREE_DW)
@@ -437,7 +465,7 @@ int mt_match_prefix(struct sip_msg *msg, m_tree_t *it,
 			return -1;
 		}
 
-		if(itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalue.s!=NULL)
+		if(itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalues!=NULL)
 		{
 			dw = (mt_dw_t*)itn[_mt_char_table[(unsigned int)tomatch->s[l]]].data;
 			while(dw) {
@@ -503,24 +531,29 @@ int mt_match_prefix(struct sip_msg *msg, m_tree_t *it,
 void mt_free_node(mt_node_t *pn, int type)
 {
 	int i;
+        mt_is_t *tvalues, *next;
+
 	if(pn==NULL)
 		return;
 
-	for(i=0; i<MT_NODE_SIZE; i++)
-	{
-		if(pn[i].tvalue.s!=NULL)
-		{
-			shm_free(pn[i].tvalue.s);
-			pn[i].tvalue.s   = NULL;
-			pn[i].tvalue.len = 0;
-			if(type==MT_TREE_DW)
-				mt_node_unset_payload(&pn[i], type);
-		}
-		if(pn[i].child!=NULL)
-		{
-			mt_free_node(pn[i].child, type);
-			pn[i].child = NULL;
+	for(i=0; i<MT_NODE_SIZE; i++) {
+	    tvalues = pn[i].tvalues;
+	    while (tvalues != NULL) {
+		if ((type == MT_TREE_SVAL) && (tvalues->tvalue.s.s != NULL)) {
+		    shm_free(tvalues->tvalue.s.s);
+		    tvalues->tvalue.s.s   = NULL;
+		    tvalues->tvalue.s.len = 0;
 		}
+		next = tvalues->next;
+		shm_free(tvalues);
+		tvalues = next;
+	    }
+	    if(type==MT_TREE_DW)
+		mt_node_unset_payload(&pn[i], type);
+	    if(pn[i].child!=NULL) {
+		mt_free_node(pn[i].child, type);
+		pn[i].child = NULL;
+	    }
 	}
 	shm_free(pn);
 	pn = NULL;
@@ -547,9 +580,10 @@ void mt_free_tree(m_tree_t *pt)
 	return;
 }
 
-int mt_print_node(mt_node_t *pn, char *code, int len)
+int mt_print_node(mt_node_t *pn, char *code, int len, int type)
 {
 	int i;
+	mt_is_t *tvalues;
 
 	if(pn==NULL || code==NULL || len>=MT_MAX_DEPTH)
 		return 0;
@@ -557,10 +591,17 @@ int mt_print_node(mt_node_t *pn, char *code, int len)
 	for(i=0; i<MT_NODE_SIZE; i++)
 	{
 		code[len]=mt_char_list.s[i];
-		if(pn[i].tvalue.s!=NULL)
-			LM_DBG("[%.*s] [%.*s]\n",
-					len+1, code, pn[i].tvalue.len, pn[i].tvalue.s);
-		mt_print_node(pn[i].child, code, len+1);
+		tvalues = pn[i].tvalues;
+		while (tvalues != NULL) {
+		    if (type == MT_TREE_IVAL) {
+			LM_INFO("[%.*s] [i:%d]\n",	len+1, code, tvalues->tvalue.n);
+		    } else if (tvalues->tvalue.s.s != NULL) {
+			LM_INFO("[%.*s] [s:%.*s]\n",
+			       len+1, code, tvalues->tvalue.s.len, tvalues->tvalue.s.s);
+		    }
+		    tvalues = tvalues->next;
+		}
+		mt_print_node(pn[i].child, code, len+1, type);
 	}
 
 	return 0;
@@ -577,9 +618,9 @@ int mt_print_tree(m_tree_t *pt)
 		return 0;
 	}
 	
-	LM_DBG("[%.*s]\n", pt->tname.len, pt->tname.s);
+	LM_INFO("[%.*s]\n", pt->tname.len, pt->tname.s);
 	len = 0;
-	mt_print_node(pt->head, mt_code_buf, len);
+	mt_print_node(pt->head, mt_code_buf, len, pt->type);
 	return mt_print_tree(pt->next);
 }
 
@@ -594,7 +635,7 @@ int mt_node_set_payload(mt_node_t *node, int type)
 
 	if(type!=MT_TREE_DW)
 		return 0;
-	s = node->tvalue;
+	s = node->tvalues->tvalue.s;
 	if(s.s[s.len-1]==';')
 		s.len--;
 	if(parse_params(&s, CLASS_ANY, &hooks, &list)<0)
@@ -690,8 +731,7 @@ int mt_table_spec(char* val)
 	if(tmp.tname.s==NULL)
 	{
 		LM_ERR("invalid mtree name\n");
-		free_params(params_list);
-		return -1;
+		goto error;
 	}
 	if(tmp.dbtable.s==NULL)
 	{
@@ -699,8 +739,10 @@ int mt_table_spec(char* val)
 		tmp.dbtable.s = "mtree";
 		tmp.dbtable.len = 5;
 	}
-	if(tmp.type!=1)
-		tmp.type = 0;
+	if ((tmp.type != 0) && (tmp.type != 1) && (tmp.type != 2)) {
+	    LM_ERR("unknown tree type <%d>\n", tmp.type);
+	    goto error;
+	}
 	
 	/* check for same tree */
 	if(_ptree == 0)

+ 19 - 8
modules/mtree/mtree.h

@@ -29,7 +29,13 @@
 #include "../../parser/msg_parser.h"
 
 #define MT_TREE_SVAL	0	
-#define MT_TREE_DW		1
+#define MT_TREE_DW	1
+#define MT_TREE_IVAL	2
+
+typedef union {
+    int n;
+    str s;
+} is_t;
 
 typedef struct _mt_dw
 {
@@ -38,11 +44,17 @@ typedef struct _mt_dw
 	struct _mt_dw *next;
 } mt_dw_t;
 
+typedef struct _mt_is
+{
+    is_t tvalue;
+    struct _mt_is *next;
+} mt_is_t;
+
 typedef struct _mt_node
 {
-	str tvalue;
-	void *data;
-	struct _mt_node *child;
+    mt_is_t *tvalues;
+    void *data;
+    struct _mt_node *child;
 } mt_node_t;
 
 #define MT_MAX_DEPTH	32
@@ -58,20 +70,19 @@ typedef struct _m_tree
 	unsigned int nritems;
 	unsigned int memsize;
 	mt_node_t *head;
-
 	struct _m_tree *next;
 } m_tree_t;
 
 
 /* prefix tree operations */
-int mt_add_to_tree(m_tree_t *pt, str *tprefix, str *tvalue);
+int mt_add_to_tree(m_tree_t *pt, str *tprefix, str *svalue);
 
 m_tree_t* mt_get_tree(str *tname);
 m_tree_t* mt_get_first_tree();
 
-str* mt_get_tvalue(m_tree_t *pt, str *tomatch, int *plen);
+is_t* mt_get_tvalue(m_tree_t *pt, str *tomatch);
 int mt_match_prefix(struct sip_msg *msg, m_tree_t *pt,
-		str *tomatch, int mode);
+		    str *tomatch, int mode);
 
 m_tree_t* mt_init_tree(str* tname, str* dbtable, int type);
 void mt_free_tree(m_tree_t *pt);

+ 21 - 6
modules/mtree/mtree_mod.c

@@ -93,6 +93,7 @@ pv_spec_t pv_weight;
 pv_spec_t pv_count;
 int _mt_tree_type = MT_TREE_SVAL;
 int _mt_ignore_duplicates = 0;
+int _mt_allow_duplicates = 0;
 
 /* lock, ref counter and flag used for reloading the date */
 static gen_lock_t *mt_lock = 0;
@@ -141,6 +142,7 @@ static param_export_t params[]={
 	{"pv_count",       STR_PARAM, &count_param.s},
 	{"mt_tree_type",   INT_PARAM, &_mt_tree_type},
 	{"mt_ignore_duplicates", INT_PARAM, &_mt_ignore_duplicates},
+	{"mt_allow_duplicates", INT_PARAM, &_mt_allow_duplicates},
 	{0, 0, 0}
 };
 
@@ -237,7 +239,7 @@ static int mod_init(void)
 		LM_ERR("invalid prefix char list\n");
 		return -1;
 	}
-	LM_INFO("mt_char_list=%s \n", mt_char_list.s);
+	LM_DBG("mt_char_list=%s \n", mt_char_list.s);
 	mt_char_table_init();
 
 	/* binding to mysql module */
@@ -805,6 +807,8 @@ int mt_print_mi_node(m_tree_t *tree, mt_node_t *pt, struct mi_node* rpl,
 	int i;
 	struct mi_node* node = NULL;
 	struct mi_attr* attr= NULL;
+	mt_is_t *tvalues;
+	str val;
 
 	if(pt==NULL || len>=MT_MAX_DEPTH)
 		return 0;
@@ -812,7 +816,8 @@ int mt_print_mi_node(m_tree_t *tree, mt_node_t *pt, struct mi_node* rpl,
 	for(i=0; i<MT_NODE_SIZE; i++)
 	{
 		code[len]=mt_char_list.s[i];
-		if(pt[i].tvalue.s!=NULL)
+		tvalues = pt[i].tvalues;
+		if (tvalues != NULL)
 		{
 			node = add_mi_node_child(rpl, 0, "MT", 2, 0, 0);
 			if(node == NULL)
@@ -825,11 +830,21 @@ int mt_print_mi_node(m_tree_t *tree, mt_node_t *pt, struct mi_node* rpl,
 						code, len+1);
 			if(attr == NULL)
 				goto error;
-					
-			attr = add_mi_attr(node, MI_DUP_VALUE, "TVALUE", 6,
-						pt[i].tvalue.s, pt[i].tvalue.len);
-			if(attr == NULL)
+
+			while (tvalues != NULL) {
+			    if (tree->type == MT_TREE_IVAL) {
+				val.s = int2str(tvalues->tvalue.n, &val.len);
+				attr = add_mi_attr(node, MI_DUP_VALUE, "TVALUE", 6,
+						   val.s, val.len);
+			    } else {
+				attr = add_mi_attr(node, MI_DUP_VALUE, "TVALUE", 6,
+						   tvalues->tvalue.s.s,
+						   tvalues->tvalue.s.len);
+			    }
+			    if(attr == NULL)
 				goto error;
+			    tvalues = tvalues->next;
+			}
 		}
 		if(mt_print_mi_node(tree, pt[i].child, rpl, code, len+1)<0)
 			goto error;

+ 96 - 1
modules_k/textops/textops.c

@@ -124,6 +124,8 @@ static int set_rpl_body_f(struct sip_msg* msg, char*, char *);
 static int is_method_f(struct sip_msg* msg, char* , char *);
 static int has_body_f(struct sip_msg *msg, char *type, char *str2 );
 static int is_privacy_f(struct sip_msg *msg, char *privacy, char *str2 );
+static int in_list_f(struct sip_msg* _msg, char* _subject, char* _list,
+		     char* _sep);
 static int cmp_str_f(struct sip_msg *msg, char *str1, char *str2 );
 static int cmp_istr_f(struct sip_msg *msg, char *str1, char *str2 );
 static int starts_with_f(struct sip_msg *msg, char *str1, char *str2 );
@@ -137,6 +139,8 @@ static int fixup_method(void** param, int param_no);
 static int add_header_fixup(void** param, int param_no);
 static int fixup_body_type(void** param, int param_no);
 static int fixup_privacy(void** param, int param_no);
+static int fixup_in_list(void** param, int param_no);
+static int fixup_free_in_list(void** param, int param_no);
 int fixup_regexpNL_none(void** param, int param_no);
 
 static int mod_init(void);
@@ -242,6 +246,9 @@ static cmd_export_t cmds[]={
 	{"is_privacy",       (cmd_function)is_privacy_f,      1,
 		fixup_privacy, 0,
 		ANY_ROUTE},
+	{"in_list", (cmd_function)in_list_f, 3, fixup_in_list,
+	        fixup_free_in_list,
+	        ANY_ROUTE},
 	{"cmp_str",  (cmd_function)cmp_str_f, 2,
 		fixup_spve_spve, 0,
 		ANY_ROUTE},
@@ -1772,6 +1779,42 @@ static int fixup_privacy(void** param, int param_no)
     return 0;
 }
 
+/*
+ * Fix in_list params: subject and list (strings that may contain pvars),
+ * separator (string)
+ */
+static int fixup_in_list(void** param, int param_no)
+{
+    if ((param_no == 1) || (param_no == 2)) return fixup_spve_null(param, 1);
+    
+    if (param_no == 3) {
+	if ((strlen((char *)*param) != 1) || (*((char *)(*param)) == 0)) {
+	    LM_ERR("invalid separator parameter\n");
+ 	    return -1;
+ 	}
+ 	return 0;
+    }
+ 
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+ 
+/*
+ * Free in_list params
+ */
+static int fixup_free_in_list(void** param, int param_no)
+{
+    if ((param_no == 1) || (param_no == 2)) {
+ 	LM_WARN("free function has not been defined for spve\n");
+ 	return 0;
+    }
+ 
+    if (param_no == 3) return 0;
+     
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
 static int add_header_fixup(void** param, int param_no)
 {
 	if(param_no==1)
@@ -1853,7 +1896,6 @@ static int has_body_f(struct sip_msg *msg, char *type, char *str2 )
 	return 1;
 }
 
-
 static int is_privacy_f(struct sip_msg *msg, char *_privacy, char *str2 )
 {
     if (parse_privacy(msg) == -1)
@@ -1863,6 +1905,59 @@ static int is_privacy_f(struct sip_msg *msg, char *_privacy, char *str2 )
 
 }
 
+/* 
+ * Checks if subject is found in list
+ */
+int in_list_f(struct sip_msg* _m, char* _subject, char* _list, char* _sep)
+{
+    str subject, list;
+    int sep;
+    char *at, *past, *s;
+
+    if (fixup_get_svalue(_m, (gparam_p)_subject, &subject) != 0) {
+	LM_ERR("cannot get subject value\n");
+	return -1;
+    } else {
+	if (subject.len == 0) {
+	    LM_ERR("subject cannot be empty string\n");
+	    return -1;
+	}
+    }
+
+    if (fixup_get_svalue(_m, (gparam_p)_list, &list) != 0) {
+	LM_ERR("cannot get list value\n");
+	return -1;
+    } else {
+	if (list.len == 0) return -1;
+    }
+
+    sep = _sep[0];
+
+    at = list.s;
+    past = list.s + list.len;
+
+    while (at < past) {
+	s = index(at, sep);
+	if (s == NULL) {
+	    if ((subject.len == (past - at)) &&
+		strncmp(at, subject.s, subject.len) == 0) {
+		return 1;
+	    } else {
+		return -1;
+	    }
+	} else {
+	    if ((subject.len == (s - at)) &&
+		strncmp(at, subject.s, subject.len) == 0) {
+		return 1;
+	    } else {
+		at = s + 1;
+	    }
+	}
+    }
+
+    return -1;
+}
+
 static int cmp_str_f(struct sip_msg *msg, char *str1, char *str2 )
 {
 	str s1;