浏览代码

Merge branch 'master' of ssh://git.sip-router.org/sip-router

Do not read this message. It's not needed. Just a stupid swede making mistakes.
Do not read this message. It's not needed. Just a stupid swede making mistakes.
Do not read this message. It's not needed. Just a stupid swede making mistakes.
Do not read this message. It's not needed. Just a stupid swede making mistakes.

* 'master' of ssh://git.sip-router.org/sip-router:
  modules_s/permissions: moved to obsolete folder
  modules_s/textops: moved to obsolete folder
  textopsx: added functions that operate on header value
  modules_k/domain: bind_domain api function takes one param
  modules_k/htable: removed unused variable
  nathelper(k): new test 128 to check port in contact against source port
  modules_s/nathelper: moved to obsolete folder
  nathelper(k): added the select for rewriting the contact
  core: proper pv buffer reinitialization
  dialog(k): Reworked dlg_set_timeout_by_profile() code to change dialog timeouts outside of a profile lock.
  modules/app_lua: Updated app_lua to support URI lookup in registrar
  modules_k/registrar: Extended C-API to include a URI lookup
  pkg/kamailio/(centos|fedora): Added more modules moved from modules_s to modules to the build
Olle E. Johansson 12 年之前
父节点
当前提交
95805adb74
共有 77 个文件被更改,包括 2302 次插入34 次删除
  1. 18 4
      modules/app_lua/app_lua_exp.c
  2. 234 0
      modules/textopsx/README
  3. 361 0
      modules/textopsx/doc/functions.xml
  4. 1394 0
      modules/textopsx/textopsx.c
  5. 67 11
      modules_k/dialog/dlg_profile.c
  6. 1 1
      modules_k/domain/api.h
  7. 1 1
      modules_k/domain/domain_mod.c
  8. 0 1
      modules_k/htable/htable.c
  9. 36 8
      modules_k/nathelper/README
  10. 27 0
      modules_k/nathelper/doc/nathelper_admin.xml
  11. 110 1
      modules_k/nathelper/nathelper.c
  12. 16 0
      modules_k/registrar/api.c
  13. 4 0
      modules_k/registrar/api.h
  14. 0 0
      obsolete/nathelper/Makefile
  15. 0 0
      obsolete/nathelper/README
  16. 0 0
      obsolete/nathelper/TODO
  17. 0 0
      obsolete/nathelper/doc/Makefile
  18. 0 0
      obsolete/nathelper/doc/functions.xml
  19. 0 0
      obsolete/nathelper/doc/nathelper.xml
  20. 0 0
      obsolete/nathelper/doc/params.xml
  21. 0 0
      obsolete/nathelper/nathelper.c
  22. 0 0
      obsolete/nathelper/nathelper.cfg
  23. 0 0
      obsolete/nathelper/nathelper.h
  24. 0 0
      obsolete/nathelper/natping.c
  25. 0 0
      obsolete/nathelper/nhelpr_funcs.c
  26. 0 0
      obsolete/nathelper/nhelpr_funcs.h
  27. 0 0
      obsolete/permissions/Makefile
  28. 0 0
      obsolete/permissions/README
  29. 0 0
      obsolete/permissions/allow_files.c
  30. 0 0
      obsolete/permissions/allow_files.h
  31. 0 0
      obsolete/permissions/config/permissions.allow
  32. 0 0
      obsolete/permissions/config/permissions.deny
  33. 0 0
      obsolete/permissions/config/register.allow
  34. 0 0
      obsolete/permissions/config/register.deny
  35. 0 0
      obsolete/permissions/doc/Makefile
  36. 0 0
      obsolete/permissions/doc/functions.xml
  37. 0 0
      obsolete/permissions/doc/params.xml
  38. 0 0
      obsolete/permissions/doc/permissions.xml
  39. 0 0
      obsolete/permissions/doc/xmlrpc.xml
  40. 0 0
      obsolete/permissions/im_db.c
  41. 0 0
      obsolete/permissions/im_db.h
  42. 0 0
      obsolete/permissions/im_hash.c
  43. 0 0
      obsolete/permissions/im_hash.h
  44. 0 0
      obsolete/permissions/im_locks.c
  45. 0 0
      obsolete/permissions/im_locks.h
  46. 0 0
      obsolete/permissions/im_rpc.c
  47. 0 0
      obsolete/permissions/im_rpc.h
  48. 0 0
      obsolete/permissions/ip_set.c
  49. 0 0
      obsolete/permissions/ip_set.h
  50. 0 0
      obsolete/permissions/ip_set_rpc.c
  51. 0 0
      obsolete/permissions/ip_set_rpc.h
  52. 0 0
      obsolete/permissions/ip_tree.c
  53. 0 0
      obsolete/permissions/ip_tree.h
  54. 0 0
      obsolete/permissions/ipmatch.c
  55. 0 0
      obsolete/permissions/ipmatch.h
  56. 0 0
      obsolete/permissions/parse_config.c
  57. 0 0
      obsolete/permissions/parse_config.h
  58. 0 0
      obsolete/permissions/permissions.c
  59. 0 0
      obsolete/permissions/permissions.h
  60. 0 0
      obsolete/permissions/permissions_rpc.h
  61. 0 0
      obsolete/permissions/rule.c
  62. 0 0
      obsolete/permissions/rule.h
  63. 0 0
      obsolete/permissions/trusted.c
  64. 0 0
      obsolete/permissions/trusted.h
  65. 0 0
      obsolete/permissions/trusted_hash.c
  66. 0 0
      obsolete/permissions/trusted_hash.h
  67. 0 0
      obsolete/permissions/trusted_rpc.c
  68. 0 0
      obsolete/permissions/trusted_rpc.h
  69. 0 0
      obsolete/textops/Makefile
  70. 0 0
      obsolete/textops/README
  71. 0 0
      obsolete/textops/doc/Makefile
  72. 0 0
      obsolete/textops/doc/functions.xml
  73. 0 0
      obsolete/textops/doc/params.xml
  74. 0 0
      obsolete/textops/doc/textops.xml
  75. 0 0
      obsolete/textops/textops.c
  76. 23 4
      pkg/kamailio/fedora/16/kamailio.spec
  77. 10 3
      pvapi.c

+ 18 - 4
modules/app_lua/app_lua_exp.c

@@ -1313,7 +1313,8 @@ static int lua_sr_registrar_save(lua_State *L)
 static int lua_sr_registrar_lookup(lua_State *L)
 {
 	int ret;
-	char *table;
+	char *table = NULL;
+	str uri = {NULL, 0};
 	sr_lua_env_t *env_L;
 
 	env_L = sr_lua_env_get();
@@ -1328,18 +1329,31 @@ static int lua_sr_registrar_lookup(lua_State *L)
 		LM_WARN("invalid parameters from Lua env\n");
 		return app_lua_return_error(L);
 	}
-	if(lua_gettop(L)!=1)
+	if(lua_gettop(L)==1)
+	{
+		table = (char*)lua_tostring(L, -1);
+	}
+	else if (lua_gettop(L)==2)
+	{
+		table = (char*)lua_tostring(L, -2);
+		uri.s = (char*)lua_tostring(L, -1);
+		uri.len = strlen(uri.s);
+	} else
 	{
 		LM_WARN("invalid number of parameters from Lua\n");
 		return app_lua_return_error(L);
 	}
-	table  = (char*)lua_tostring(L, -1);
 	if(table==NULL || strlen(table)==0)
 	{
 		LM_WARN("invalid parameters from Lua\n");
 		return app_lua_return_error(L);
 	}
-	ret = _lua_registrarb.lookup(env_L->msg, table);
+	if(lua_gettop(L)==2)
+	{
+		ret = _lua_registrarb.lookup_uri(env_L->msg, table, &uri);
+	} else {
+		ret = _lua_registrarb.lookup(env_L->msg, table);
+	}
 
 	return app_lua_return_int(L, ret);
 }

+ 234 - 0
modules/textopsx/README

@@ -20,6 +20,20 @@ Daniel-Constantin Mierla
         1.2.3. remove_body()
         1.2.4. keep_hf(regexp)
         1.2.5. fnmatch(value, expr [, flags])
+        1.2.6. append_hf_value(hf, hvalue)
+        1.2.7. insert_hf_value(hf, hvalue)
+        1.2.8. remove_hf_value(hf_par)
+        1.2.9. remove_hf_value2(hf_par)
+        1.2.10. assign_hf_value(hf, hvalue)
+        1.2.11. assign_hf_value2(hf, hvalue)
+        1.2.12. include_hf_value(hf, hvalue)
+        1.2.13. exclude_hf_value(hf, hvalue)
+        1.2.14. hf_value_exists(hf, hvalue)
+        1.2.15. Selects
+
+              1.2.15.1. @hf_value
+              1.2.15.2. @hf_value2
+              1.2.15.3. @hf_value_exists
 
 1.1. Overview
 
@@ -115,3 +129,223 @@ if(fnmatch("$rU", "123*"))
     ...
 }
 ...
+
+1.2.6. append_hf_value(hf, hvalue)
+
+   Append new header value after an existing header, if no index acquired
+   append at the end of list. Note that a header may consist of comma
+   delimited list of values.
+
+   Meaning of the parameters is as follows:
+     * hf - Header field to be appended. Format: HFNAME [ [IDX] ]. If
+       index is not specified new header is inserted at the end of
+       message.
+     * hvalue - Value to be added, config var formatting supported.
+
+   Example 6. append_hf_value usage
+...
+append_hf_value("foo", "gogo;stamp=$Ts")   # add new header
+append_hf_value("foo[1]", "gogo")  # add new value behind first value
+append_hf_value("foo[-1]", "$var(Bar)") # try add value to the last header, if n
+ot exists add new header
+...
+
+1.2.7. insert_hf_value(hf, hvalue)
+
+   Insert new header value before an existing header, if no index acquired
+   insert before first hf header. Note that a header may consist of comma
+   delimited list of values. To insert value behing last value use
+   appenf_hf_value.
+
+   Meaning of the parameters is as follows:
+     * hf - Header field to be appended. Format: HFNAME [ [IDX] ]. If
+       index is not specified new header is inserted at the top of
+       message.
+     * hvalue - Value to be added, config var formatting supported.
+
+   Example 7. insert_hf_value usage
+...
+insert_hf_value("foo[2]", "gogo")
+insert_hf_value("foo", "$avp(foo)")   # add new header at the top of list
+insert_hf_value("foo[1]", "gogo") # try add to the first header
+...
+
+1.2.8. remove_hf_value(hf_par)
+
+   Remove the header value from existing header, Note that a header may
+   consist of comma delimited list of values.
+
+   Meaning of the parameters is as follows:
+     * hf_par - Header field/param to be removed. Format: HFNAME [ [IDX] ]
+       [. PARAM ] If asterisk is specified as index then all values are
+       affected.
+
+   Example 8. remove_hf_value usage
+...
+remove_hf_value("foo")  # remove foo[1]
+remove_hf_value("foo[*]")  # remove all foo's headers
+remove_hf_value("foo[-1]") # last foo
+remove_hf_value("foo.bar")  # delete parameter
+remove_hf_value("foo[*].bar") # for each foo delete bar parameters
+...
+
+1.2.9. remove_hf_value2(hf_par)
+
+   Remove specified header or parameter. It is expected header in
+   Authorization format (comma delimiters are not treated as multi-value
+   delimiters).
+
+   Meaning of the parameters is as follows:
+     * hf_par - Header/param to be removed. Format: HFNAME [ [IDX] ] [.
+       PARAM ] If asterisk is specified as index then all values are
+       affected.
+
+   Example 9. remove_hf_value2 usage
+...
+remove_hf_value2("foo")  # remove foo[1]
+remove_hf_value2("foo[*]")  # remove all foo's headers, the same as remove_hf_he
+ader("foo[*]");
+remove_hf_value2("foo[-1]") # last foo
+remove_hf_value2("foo.bar")  # delete parameter
+remove_hf_value2("foo[*].bar") # for each foo delete bar parameters
+...
+
+1.2.10. assign_hf_value(hf, hvalue)
+
+   Assign value to specified header value / param.
+
+   Meaning of the parameters is as follows:
+     * hf_para - Header field value / param to be appended. Format: HFNAME
+       [ [IDX] ] [. PARAM] If asterisk is specified as index then all
+       values are affected.
+     * hvalue - Value to be assigned, config var formatting supported. If
+       value is empty then no equal sign apears in param.
+
+   Example 10. assign_hf_value usage
+...
+assign_hf_value("foo", "gogo")  # foo[1]
+assign_hf_value("foo[-1]", "gogo")  # foo[last_foo]
+
+assign_hf_value("foo.bar", "")
+assign_hf_value("foo[3].bar", "")
+assign_hf_value("foo[*]", "")  # remove all foo's, empty value remains
+assign_hf_value("foo[*].bar", "")  # set empty value (ex. lr)
+...
+
+1.2.11. assign_hf_value2(hf, hvalue)
+
+   Assign value to specified header. It is expected header in
+   Authorization format (comma delimiters are not treated as multi-value
+   delimiters).
+
+   Meaning of the parameters is as follows:
+     * hf_para - Header field value / param to be appended. Format: HFNAME
+       [ [IDX] ] [. PARAM] If asterisk is specified as index then all
+       values are affected.
+     * hvalue - Value to be assigned, config var formatting supported. If
+       value is empty then no equal sign apears in param.
+
+   Example 11. assign_hf_value2 usage
+...
+assign_hf_value2("Authorization.integrity-protected", "\"yes\"")
+assign_hf_value2("foo[-1]", "gogo")  # foo[last_foo]
+assign_hf_value2("foo[*].bar", "")  # set empty value (ex. lr)
+...
+
+1.2.12. include_hf_value(hf, hvalue)
+
+   Add value in set if not exists, eg. "Supported: path,100rel".
+
+   Meaning of the parameters is as follows:
+     * hf - Header field name to be affected.
+     * hvalue - config var formatting supported.
+
+   Example 12. include_hf_value usage
+...
+include_hf_value("Supported", "path");
+...
+
+1.2.13. exclude_hf_value(hf, hvalue)
+
+   Remove value from set if exists, eg. "Supported: path,100rel".
+
+   Meaning of the parameters is as follows:
+     * hf - Header field name to be affected.
+     * hvalue - config formatting supported.
+
+   Example 13. exclude_hf_value usage
+...
+exclude_hf_value("Supported", "100rel");
+...
+
+1.2.14. hf_value_exists(hf, hvalue)
+
+   Check if value exists in set. Alternate select
+   @hf_value_exists.HF.VALUE may be used. It returns one or zero.
+
+   Meaning of the parameters is as follows:
+     * hf - Header field name to be affected. Underscores are treated as
+       dashes.
+     * hvalue - config var formatting supported.
+
+   Example 14. hf_value_exists usage
+...
+if (hf_value_exists("Supported", "100rel")) {
+
+}
+
+if (@hf_value_exists.supported.path == "1") {
+
+}
+...
+
+1.2.15. Selects
+
+1.2.15.1. @hf_value
+
+   Get value of required header-value or param. Note that functions called
+   'value2' works with Authorization-like headers where comma is not
+   treated as value delimiter. Formats: @hf_value.HFNAME[IDX] # idx value,
+   negative value counts from bottom @hf_value.HFNAME.PARAM_NAME
+   @hf_value.HFNAME[IDX].PARAM_NAME @hf_value.HFNAME.p.PARAM_NAME # or
+   .param., useful if requred called "uri", "p", "param"
+   @hf_value.HFNAME[IDX].p.PARAM_NAME # dtto @hf_value.HFNAME[IDX].uri #
+   (< & > excluded) @hf_value.HFNAME[*] # return comma delimited list of
+   all values (combines headers) @hf_value.HFNAME # the same as above [*]
+   but may be parsed by cfg.y @hf_value.HFNAME[*].uri # return comma
+   delimited list of uris (< & > excluded) @hf_value.HFNAME.uri # the same
+   as above [*] but may be parsed by cfg.y @hf_value.HFNAME[IDX].name #
+   returns name part, quotes excluded @hf_value.HFNAME.name # returns name
+   part of the first value @hf_value2.HFNAME # returns value of first
+   header @hf_value2.HFNAME[IDX] # returns value of idx's header
+   @hf_value2.HFNAME.PARAM_NAME @hf_value2.HFNAME[IDX].PARAM_NAME
+   @hf_value.HFNAME[IDX].uri # return URI, quotes excluded
+   @hf_value.HFNAME.p.uri # returns param named uri, not URI itself
+   @hf_value.HFNAME.p.name # returns param named name, not name itself
+   @hf_value.HFNAME[IDX].uri.name # any sel_any_uri nested features may be
+   used @hf_value.HFNAME[IDX].nameaddr.name # select_any_nameaddr
+
+   Meaning of the parameters is as follows:
+     * HFNAME - Header field name. Underscores are treated as dashes.
+     * IDX - Value index, negative value counts from bottom
+     * PARAM_NAME - name of parameter
+
+   Example 15. @hf_value select usage
+...
+$a = @hf_value.my_header[1].my_param;
+xplog("L_ERR", "$sel(@hf_value.via[-1]), $sel(@hf_value.from.tag)\n");
+$b = @hf_value.p_associated_uri;
+
+xplog("L_ERR", "Route uris: '$sel(@hf_value.route[*].uri)'\n");
+$rr = @hf_value.route.uri;
+
+$prt = @hf_value2.authorization.integrity_protected;
+...
+
+1.2.15.2. @hf_value2
+
+   TBA.
+
+1.2.15.3. @hf_value_exists
+
+   TBA.

+ 361 - 0
modules/textopsx/doc/functions.xml

@@ -146,4 +146,365 @@ if(fnmatch("$rU", "123*"))
 		</example>
 	</section>
 
+    <section id="append_hf_value">
+	<title>
+	    <function>append_hf_value(hf, hvalue)</function>
+	</title>
+	<para>
+		Append new header value after an existing header, if no index acquired append at the end of
+		list. Note that a header may consist of comma delimited list of values.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>hf</emphasis> - Header field to be appended. Format: HFNAME [ [IDX] ].
+		If index is not specified new header is inserted at the end of message.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>hvalue</emphasis> - Value to be added, config var formatting supported.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>append_hf_value</function> usage</title>
+	    <programlisting>
+...
+append_hf_value("foo", "gogo;stamp=$Ts")   # add new header
+append_hf_value("foo[1]", "gogo")  # add new value behind first value
+append_hf_value("foo[-1]", "$var(Bar)") # try add value to the last header, if not exists add new header
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="insert_hf_value">
+	<title>
+	    <function>insert_hf_value(hf, hvalue)</function>
+	</title>
+	<para>
+		Insert new header value before an existing header, if no index acquired insert before first
+		hf header. Note that a header may consist of comma delimited list of values.
+		To insert value behing last value use <function>appenf_hf_value</function>.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>hf</emphasis> - Header field to be appended. Format: HFNAME [ [IDX] ].
+		If index is not specified new header is inserted at the top of message.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>hvalue</emphasis> - Value to be added, config var formatting supported.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>insert_hf_value</function> usage</title>
+	    <programlisting>
+...
+insert_hf_value("foo[2]", "gogo")
+insert_hf_value("foo", "$avp(foo)")   # add new header at the top of list
+insert_hf_value("foo[1]", "gogo") # try add to the first header
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="remove_hf_value">
+	<title>
+	    <function>remove_hf_value(hf_par)</function>
+	</title>
+	<para>
+		Remove the header value from existing header, Note that a header may consist of comma delimited list of values.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>hf_par</emphasis> - Header field/param to be removed. Format: HFNAME [ [IDX] ] [. PARAM ]
+		If asterisk is specified as index then all values are affected.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>remove_hf_value</function> usage</title>
+	    <programlisting>
+...
+remove_hf_value("foo")  # remove foo[1]
+remove_hf_value("foo[*]")  # remove all foo's headers
+remove_hf_value("foo[-1]") # last foo
+remove_hf_value("foo.bar")  # delete parameter
+remove_hf_value("foo[*].bar") # for each foo delete bar parameters
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="remove_hf_value2">
+	<title>
+	    <function>remove_hf_value2(hf_par)</function>
+	</title>
+	<para>
+		Remove specified header or parameter. It is expected header in Authorization format (comma delimiters are not treated as multi-value delimiters).
+
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>hf_par</emphasis> - Header/param to be removed. Format: HFNAME [ [IDX] ] [. PARAM ]
+		If asterisk is specified as index then all values are affected.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>remove_hf_value2</function> usage</title>
+	    <programlisting>
+...
+remove_hf_value2("foo")  # remove foo[1]
+remove_hf_value2("foo[*]")  # remove all foo's headers, the same as remove_hf_header("foo[*]");
+remove_hf_value2("foo[-1]") # last foo
+remove_hf_value2("foo.bar")  # delete parameter
+remove_hf_value2("foo[*].bar") # for each foo delete bar parameters
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="assign_hf_value">
+	<title>
+	    <function>assign_hf_value(hf, hvalue)</function>
+	</title>
+	<para>
+		Assign value to specified header value / param.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+			<para><emphasis>hf_para</emphasis> - Header field value / param to be appended.
+				Format: HFNAME [ [IDX] ] [. PARAM]
+		If asterisk is specified as index then all values are affected.
+		</para>
+	    </listitem>
+	    <listitem>
+			<para><emphasis>hvalue</emphasis> - Value to be assigned, config var
+				formatting supported. If value is empty then no equal sign apears in param.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>assign_hf_value</function> usage</title>
+	    <programlisting>
+...
+assign_hf_value("foo", "gogo")  # foo[1]
+assign_hf_value("foo[-1]", "gogo")  # foo[last_foo]
+
+assign_hf_value("foo.bar", "")
+assign_hf_value("foo[3].bar", "")
+assign_hf_value("foo[*]", "")  # remove all foo's, empty value remains
+assign_hf_value("foo[*].bar", "")  # set empty value (ex. lr)
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="assign_hf_value2">
+	<title>
+	    <function>assign_hf_value2(hf, hvalue)</function>
+	</title>
+	<para>
+		Assign value to specified header. It is expected header in Authorization format (comma delimiters are not treated as multi-value delimiters).
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>hf_para</emphasis> - Header field value / param to be appended. Format: HFNAME [ [IDX] ] [. PARAM]
+		If asterisk is specified as index then all values are affected.
+		</para>
+	    </listitem>
+	    <listitem>
+			<para><emphasis>hvalue</emphasis> - Value to be assigned, config var formatting supported.
+				If value is empty then no equal sign apears in param.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>assign_hf_value2</function> usage</title>
+	    <programlisting>
+...
+assign_hf_value2("Authorization.integrity-protected", "\"yes\"")
+assign_hf_value2("foo[-1]", "gogo")  # foo[last_foo]
+assign_hf_value2("foo[*].bar", "")  # set empty value (ex. lr)
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="include_hf_value">
+	<title>
+	    <function>include_hf_value(hf, hvalue)</function>
+	</title>
+	<para>
+		Add value in set if not exists, eg. "Supported: path,100rel".
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>hf</emphasis> - Header field name to be affected.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>hvalue</emphasis> - config var formatting supported.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>include_hf_value</function> usage</title>
+	    <programlisting>
+...
+include_hf_value("Supported", "path");
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="exclude_hf_value">
+	<title>
+	    <function>exclude_hf_value(hf, hvalue)</function>
+	</title>
+	<para>
+		Remove value from set if exists, eg. "Supported: path,100rel".
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>hf</emphasis> - Header field name to be affected.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>hvalue</emphasis> - config formatting supported.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>exclude_hf_value</function> usage</title>
+	    <programlisting>
+...
+exclude_hf_value("Supported", "100rel");
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="hf_value_exists">
+	<title>
+	    <function>hf_value_exists(hf, hvalue)</function>
+	</title>
+	<para>
+		Check if value exists in set. Alternate select <emphasis>@hf_value_exists.HF.VALUE</emphasis>
+		may be used. It returns one or zero.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>hf</emphasis> - Header field name to be affected. Underscores are treated as dashes.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>hvalue</emphasis> - config var formatting supported.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>hf_value_exists</function> usage</title>
+	    <programlisting>
+...
+if (hf_value_exists("Supported", "100rel")) {
+
+}
+
+if (@hf_value_exists.supported.path == "1") {
+
+}
+...
+	    </programlisting>
+	</example>
+    </section>
+
+	<section>
+	<title>Selects</title>
+    <section id="sel.hf_value">
+	<title>@hf_value</title>
+	<para>
+		Get value of required header-value or param. Note that functions called 'value2'
+		works with Authorization-like headers where comma is not treated as value delimiter. Formats:
+		@hf_value.HFNAME[IDX]    # idx value, negative value counts from bottom
+		@hf_value.HFNAME.PARAM_NAME
+		@hf_value.HFNAME[IDX].PARAM_NAME
+		@hf_value.HFNAME.p.PARAM_NAME  # or .param., useful if requred called "uri", "p", "param"
+		@hf_value.HFNAME[IDX].p.PARAM_NAME # dtto
+		@hf_value.HFNAME[IDX].uri # (&lt; &amp; &gt; excluded)
+		@hf_value.HFNAME[*]     # return comma delimited list of all values (combines headers)
+		@hf_value.HFNAME        # the same as above [*] but may be parsed by cfg.y
+		@hf_value.HFNAME[*].uri # return comma delimited list of uris (&lt; &amp; &gt; excluded)
+		@hf_value.HFNAME.uri    # the same as above [*] but may be parsed by cfg.y
+		@hf_value.HFNAME[IDX].name  # returns name part, quotes excluded
+		@hf_value.HFNAME.name   # returns name part of the first value
+
+		@hf_value2.HFNAME        # returns value of first header
+		@hf_value2.HFNAME[IDX]   # returns value of idx's header
+		@hf_value2.HFNAME.PARAM_NAME
+		@hf_value2.HFNAME[IDX].PARAM_NAME
+
+		@hf_value.HFNAME[IDX].uri  # return URI, quotes excluded
+		@hf_value.HFNAME.p.uri  # returns param named uri, not URI itself
+		@hf_value.HFNAME.p.name # returns param named name, not name itself
+		@hf_value.HFNAME[IDX].uri.name #  any sel_any_uri nested features may be used
+		@hf_value.HFNAME[IDX].nameaddr.name # select_any_nameaddr
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>HFNAME</emphasis> - Header field name. Underscores are treated as dashes.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>IDX</emphasis> - Value index, negative value counts from bottom
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>PARAM_NAME</emphasis> - name of parameter
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>@hf_value select</function> usage</title>
+	    <programlisting>
+...
+$a = @hf_value.my_header[1].my_param;
+xplog("L_ERR", "$sel(@hf_value.via[-1]), $sel(@hf_value.from.tag)\n");
+$b = @hf_value.p_associated_uri;
+
+xplog("L_ERR", "Route uris: '$sel(@hf_value.route[*].uri)'\n");
+$rr = @hf_value.route.uri;
+
+$prt = @hf_value2.authorization.integrity_protected;
+...
+	    </programlisting>
+	</example>
+    </section>
+    <section id="sel.hf_value2">
+	<title>@hf_value2</title>
+	<para>
+		TBA.
+	</para>
+    </section>
+    <section id="sel.hf_value_exists">
+	<title>@hf_value_exists</title>
+	<para>
+		TBA.
+	</para>
+    </section>
+
+    </section>
+
 </section>

+ 1394 - 0
modules/textopsx/textopsx.c

@@ -35,6 +35,10 @@
 #include "../../msg_translator.h"
 #include "../../tcp_options.h"
 #include "../../mod_fix.h"
+#include "../../parser/parse_hname2.h"
+#include "../../select.h"
+#include "../../select_buf.h"
+
 
 #include "api.h"
 
@@ -52,10 +56,26 @@ static int w_fnmatch3_f(sip_msg_t*, char*, char*, char*);
 static int fixup_fnmatch(void** param, int param_no);
 
 static int w_remove_body_f(struct sip_msg*, char*, char *);
+
+static int incexc_hf_value_f(struct sip_msg* msg, char* , char *);
+static int include_hf_value_fixup(void**, int);
+static int exclude_hf_value_fixup(void**, int);
+static int hf_value_exists_fixup(void**, int);
+
+static int insupddel_hf_value_f(struct sip_msg* msg, char* _hname, char* _val);
+static int append_hf_value_fixup(void** param, int param_no);
+static int insert_hf_value_fixup(void** param, int param_no);
+static int remove_hf_value_fixup(void** param, int param_no);
+static int assign_hf_value_fixup(void** param, int param_no);
+static int remove_hf_value2_fixup(void** param, int param_no);
+static int assign_hf_value2_fixup(void** param, int param_no);
+
 static int bind_textopsx(textopsx_api_t *tob);
 
 static int mod_init(void);
 
+extern select_row_t sel_declaration[];
+
 /* cfg functions */
 static cmd_export_t cmds[] = {
 	{"msg_apply_changes",    (cmd_function)msg_apply_changes_f,     0,
@@ -70,6 +90,26 @@ static cmd_export_t cmds[] = {
 		fixup_fnmatch, ANY_ROUTE },
 	{"fnmatch",              (cmd_function)w_fnmatch3_f,            3,
 		fixup_fnmatch, ANY_ROUTE },
+
+	{"append_hf_value",        insupddel_hf_value_f,         2, append_hf_value_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+	{"insert_hf_value",        insupddel_hf_value_f,         2, insert_hf_value_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+	{"remove_hf_value",        insupddel_hf_value_f,         1, remove_hf_value_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+	{"assign_hf_value",        insupddel_hf_value_f,         2, assign_hf_value_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+	{"remove_hf_value2",       insupddel_hf_value_f,         1, remove_hf_value2_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+	{"assign_hf_value2",       insupddel_hf_value_f,         2, assign_hf_value2_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+	{"include_hf_value", incexc_hf_value_f,      2, include_hf_value_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+	{"exclude_hf_value", incexc_hf_value_f,      2, exclude_hf_value_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+	{"hf_value_exists",  incexc_hf_value_f,      2, hf_value_exists_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+
 	{"bind_textopsx",        (cmd_function)bind_textopsx,           1,
 		0, ANY_ROUTE },
 
@@ -99,6 +139,7 @@ static int mod_init(void)
 #ifdef USE_TCP
 	tcp_set_clone_rcvbuf(1);
 #endif
+	register_select_table(sel_declaration);
 	return 0;
 }
 
@@ -428,3 +469,1356 @@ static int bind_textopsx(textopsx_api_t *tob){
 	tob->msg_apply_changes = msg_apply_changes_f;
 	return 0;
 }
+
+
+/**
+ * functions operating on header value
+ */
+#define HNF_ALL 0x01
+#define HNF_IDX 0x02
+
+#define MAX_HF_VALUE_STACK 10
+
+enum {hnoInsert, hnoAppend, hnoAssign, hnoRemove, hnoInclude, hnoExclude, hnoIsIncluded, hnoGetValue, hnoGetValueUri, hnoGetValueName, hnoRemove2, hnoAssign2, hnoGetValue2};
+
+struct hname_data {
+	int oper;
+	int htype;
+	str hname;
+	int flags;
+	int idx;
+	str param;
+};
+
+#define is_space(_p) ((_p) == '\t' || (_p) == '\n' || (_p) == '\r' || (_p) == ' ')
+
+#define eat_spaces(_p) \
+	while( is_space(*(_p)) ){\
+	(_p)++;}
+
+#define is_alphanum(_p) (((_p) >= 'a' && (_p) <= 'z') || ((_p) >= 'A' && (_p) <= 'Z') || ((_p) >= '0' && (_p) <= '9') || (_p) == '_' || (_p) == '-')
+
+#define eat_while_alphanum(_p) \
+	while ( is_alphanum(*(_p)) ) {\
+		(_p)++; }
+
+static int fixup_hvalue_param(void** param, int param_no) {
+	return fixup_spve_null(param, 1);
+}
+
+static int eval_hvalue_param(sip_msg_t *msg, gparam_t *val, str *s) {
+	if(fixup_get_svalue(msg, val, s)<0) {
+		LM_ERR("could not get string param value\n");
+		return E_UNSPEC;
+	}
+	return 1;
+}
+
+/* parse:  hname [ ([] | [*] | [number]) ] [ "." param ] */
+static int fixup_hname_param(char *hname, struct hname_data** h) {
+	struct hdr_field hdr;
+	char *savep, savec;
+
+	*h = pkg_malloc(sizeof(**h));
+	if (!*h) return E_OUT_OF_MEM;
+	memset(*h, 0, sizeof(**h));
+
+	memset(&hdr, 0, sizeof(hdr));
+	eat_spaces(hname);
+	(*h)->hname.s = hname;
+	savep = hname;
+	eat_while_alphanum(hname);
+	(*h)->hname.len = hname - (*h)->hname.s;
+	savec = *hname;
+	*hname = ':';
+	parse_hname2((*h)->hname.s, (*h)->hname.s+(*h)->hname.len+3, &hdr);
+	*hname = savec;
+
+	if (hdr.type == HDR_ERROR_T) goto err;
+	(*h)->htype = hdr.type;
+
+	eat_spaces(hname);
+	savep = hname;
+	if (*hname == '[') {
+		hname++;
+		eat_spaces(hname);
+		savep = hname;
+		(*h)->flags |= HNF_IDX;
+		if (*hname == '*') {
+			(*h)->flags |= HNF_ALL;
+			hname++;
+		}
+		else if (*hname != ']') {
+			char* c;
+			(*h)->idx = strtol(hname, &c, 10);
+			if (hname == c) goto err;
+			hname = c;
+		}
+		eat_spaces(hname);
+		savep = hname;
+		if (*hname != ']') goto err;
+		hname++;
+	}
+	eat_spaces(hname);
+	savep = hname;
+	if (*hname == '.') {
+		hname++;
+		eat_spaces(hname);
+		savep = hname;
+		(*h)->param.s = hname;
+		eat_while_alphanum(hname);
+		(*h)->param.len = hname-(*h)->param.s;
+		if ((*h)->param.len == 0) goto err;
+	}
+	else {
+		(*h)->param.s = hname;
+	}
+	savep = hname;
+	if (*hname != '\0') goto err;
+	(*h)->hname.s[(*h)->hname.len] = '\0';
+	(*h)->param.s[(*h)->param.len] = '\0';
+	return 0;
+err:
+	pkg_free(*h);
+	LOG(L_ERR, "ERROR: textops: cannot parse header near '%s'\n", savep);
+	return E_CFG;
+}
+
+static int fixup_hname_str(void** param, int param_no) {
+	if (param_no == 1) {
+		struct hname_data* h;
+		int res = fixup_hname_param(*param, &h);
+		if (res < 0) return res;
+		*param = h;
+	}
+	else if (param_no == 2) {
+		return fixup_hvalue_param(param, param_no);
+	}
+	return 0;
+}
+
+
+static int find_next_hf(struct sip_msg* msg, struct hname_data* hname, struct hdr_field** hf) {
+	if (!*hf) {
+		if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
+			LOG(L_ERR, "ERROR: textops: find_next_hf: Error while parsing message\n");
+			return -1;
+		}
+		*hf = msg->headers;
+	}
+	else {
+		*hf = (*hf)->next;
+	}
+	for (; *hf; *hf = (*hf)->next) {
+		if (hname->htype == HDR_OTHER_T) {
+			if ((*hf)->name.len==hname->hname.len && strncasecmp((*hf)->name.s, hname->hname.s, (*hf)->name.len)==0)
+				return 1;
+		}
+		else if (hname->htype == (*hf)->type) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int find_next_value(char** start, char* end, str* val, str* lump_val) {
+	int quoted = 0;
+	lump_val->s = *start;
+	while (*start < end && is_space(**start) ) (*start)++;
+	val->s = *start;
+	while ( *start < end && (**start != ',' || quoted) ) {
+		if (**start == '\"' && (!quoted || (*start)[-1]!='\\') )
+			quoted = ~quoted;
+		(*start)++;
+	}
+	val->len = *start - val->s;
+	while (val->len > 0 && is_space(val->s[val->len-1])) val->len--;
+/* we cannot automatically strip quotes!!! an example why: "name" <sip:ssss>;param="bar" 
+	if (val->len >= 2 && val->s[0] == '\"' && val->s[val->len-1] == '\"') {
+		val->s++;
+		val->len -= 2;
+	}
+*/
+	while (*start < end && **start != ',') (*start)++;
+	if (*start < end) {
+		(*start)++;
+	}
+	lump_val->len = *start - lump_val->s;
+	return (*start < end);
+}
+
+static void adjust_lump_val_for_delete(struct hdr_field* hf, str* lump_val) {
+	if ( lump_val->s+lump_val->len == hf->body.s+hf->body.len ) {
+		if (lump_val->s > hf->body.s) {
+		/* in case if is it last value in header save position of last delimiter to remove it with rightmost value */
+			lump_val->s--;
+			lump_val->len++;
+		}
+	}
+}
+
+static int find_hf_value_idx(struct sip_msg* msg, struct hname_data* hname, struct hdr_field** hf, str* val, str* lump_val) {
+	int res;
+	char *p;
+	if ( hname->flags & HNF_ALL || hname->idx == 0) return -1;
+	*hf = 0;
+	if (hname->idx > 0) {
+		int idx;
+		idx = hname->idx;
+		do {
+			res = find_next_hf(msg, hname, hf);
+			if (res < 0) return -1;
+			if (*hf) {
+				if (val) {
+					lump_val->len = 0;
+					p = (*hf)->body.s;
+					do {
+						res = find_next_value(&p, (*hf)->body.s+(*hf)->body.len, val, lump_val);
+						idx--;
+					} while (res && idx);
+				}
+				else {
+					idx--;
+				}
+			}
+		} while (*hf && idx);
+	}
+	else if (hname->idx < 0) {  /* search from the bottom */
+		struct hf_value_stack {
+			str val, lump_val;
+			struct hdr_field* hf;
+		} stack[MAX_HF_VALUE_STACK];
+		int stack_pos, stack_num;
+
+		if ( -hname->idx > MAX_HF_VALUE_STACK ) return -1;
+		stack_pos = stack_num = 0;
+		do {
+			res = find_next_hf(msg, hname, hf);
+			if (res < 0) return -1;
+			if (*hf) {
+				stack[stack_pos].lump_val.len = 0;
+				p = (*hf)->body.s;
+				do {
+					stack[stack_pos].hf = *hf;
+					if (val)
+						res = find_next_value(&p, (*hf)->body.s+(*hf)->body.len, &stack[stack_pos].val, &stack[stack_pos].lump_val);
+					else
+						res = 0;
+					stack_pos++;
+					if (stack_pos >= MAX_HF_VALUE_STACK)
+						stack_pos = 0;
+					if (stack_num < MAX_HF_VALUE_STACK)
+						stack_num++;
+
+				} while (res);
+			}
+		} while (*hf);
+
+		if (-hname->idx <= stack_num) {
+			stack_pos += hname->idx;
+			if (stack_pos < 0)
+				stack_pos += MAX_HF_VALUE_STACK;
+			*hf = stack[stack_pos].hf;
+			if (val) {
+				*val = stack[stack_pos].val;
+				*lump_val = stack[stack_pos].lump_val;
+			}
+		}
+		else {
+			*hf = 0;
+		}
+	}
+	else
+		return -1;
+	return *hf?1:0;
+}
+
+static int find_hf_value_param(struct hname_data* hname, str* param_area, str* value, str* lump_upd, str* lump_del) {
+	int i, j, found;
+
+	i = 0;
+	while (1) {
+		lump_del->s = param_area->s + i;
+		for (; i < param_area->len && is_space(param_area->s[i]); i++);
+		if (i < param_area->len && param_area->s[i] == ';') {	/* found a param ? */
+			i++;
+			for (; i < param_area->len && is_space(param_area->s[i]); i++);
+			j = i;
+			for (; i < param_area->len && !is_space(param_area->s[i]) && param_area->s[i]!='=' && param_area->s[i]!=';'; i++);
+
+			found = hname->param.len == i-j && !strncasecmp(hname->param.s, param_area->s+j, i-j);
+			lump_upd->s = param_area->s+i;
+			value->s = param_area->s+i;
+			value->len = 0;
+			for (; i < param_area->len && is_space(param_area->s[i]); i++);
+			if (i < param_area->len && param_area->s[i]=='=') {
+				i++;
+				for (; i < param_area->len && is_space(param_area->s[i]); i++);
+				value->s = param_area->s+i;
+				if (i < param_area->len) {
+					if (param_area->s[i]=='\"') {
+						i++;
+						value->s++;
+						for (; i<param_area->len; i++) {
+							if (param_area->s[i]=='\"') {
+								i++;
+								break;
+							}
+							value->len++;
+						}
+					}
+					else {
+						for (; i<param_area->len && !is_space(param_area->s[i]) && param_area->s[i]!=';'; i++, value->len++);
+					}
+				}
+			}
+			if (found) {
+				lump_del->len = param_area->s+i - lump_del->s;
+				lump_upd->len = param_area->s+i - lump_upd->s;
+				return 1;
+			}
+		}
+		else { /* not found, return last correct position, should be end of param area */
+			lump_del->len = 0;
+			return 0;
+		}
+	}
+}
+
+/* parse:  something param_name=param_value something [ "," something param_name="param_value" ....]
+ * 'something' is required by Authenticate
+ */
+static int find_hf_value2_param(struct hname_data* hname, str* param_area, str* value, str* lump_upd, str* lump_del, char* delim) {
+	int i, j, k, found, comma_flag;
+
+	i = 0;
+	*delim = 0;
+	lump_del->len = 0;
+	while (i < param_area->len) {
+
+		lump_del->s = param_area->s + i;
+		while (i<param_area->len && is_space(param_area->s[i])) i++;
+		comma_flag = i < param_area->len && param_area->s[i] == ',';
+		if (comma_flag) i++;
+		while (i<param_area->len && is_space(param_area->s[i])) i++;
+
+		if (i < param_area->len && is_alphanum(param_area->s[i])) {	/* found a param name ? */
+			j = i;
+			if (!*delim) *delim = ' ';
+			while (i<param_area->len && is_alphanum(param_area->s[i])) i++;
+
+			k = i;
+			while (i<param_area->len && is_space(param_area->s[i])) i++;
+			lump_upd->s = param_area->s + i;
+			if (i < param_area->len && param_area->s[i] == '=') {	/* if equal then it's the param */
+				*delim = ',';
+				i++;
+				found = hname->param.len == k-j && !strncasecmp(hname->param.s, param_area->s+j, k-j);
+				while (i<param_area->len && is_space(param_area->s[i])) i++;
+
+				value->s = param_area->s+i;
+				value->len = 0;
+				if (i < param_area->len) {
+					if (param_area->s[i]=='\"') {
+						i++;
+						value->s++;
+						for (; i<param_area->len; i++) {
+							if (param_area->s[i]=='\"') {
+								i++;
+								break;
+							}
+							value->len++;
+						}
+					}
+					else {
+						for (; i<param_area->len && !is_space(param_area->s[i]) && param_area->s[i]!=','; i++, value->len++);
+					}
+				}
+				if (found) {
+					lump_upd->len = param_area->s+i - lump_upd->s;
+					lump_del->len = param_area->s+i - lump_del->s;
+
+					while (i<param_area->len && is_space(param_area->s[i])) i++;
+
+					if (!comma_flag && i < param_area->len && param_area->s[i]==',') {
+						i++;
+						lump_del->len = param_area->s+i - lump_del->s;
+					}
+					return 1;
+				}
+			}
+			while (i<param_area->len && is_space(param_area->s[i])) i++;
+		}
+		else {
+			while (i<param_area->len && !is_space(param_area->s[i]) && !param_area->s[i]!=',') i++;
+		}
+	}
+	lump_del->s = param_area->s + i;
+	return 0;
+}
+
+static int insert_header_lump(struct sip_msg* msg, char* msg_position, int lump_before, str* hname, str *val) {
+	struct lump* anchor;
+	char *s;
+	int len;
+
+	anchor = anchor_lump(msg, msg_position - msg->buf, 0, 0);
+	if (anchor == 0) {
+		LOG(L_ERR, "ERROR: textops: insert_header_lump(): Can't get anchor\n");
+		return -1;
+	}
+
+	len=hname->len+2+val->len+2;
+
+	s = (char*)pkg_malloc(len);
+	if (!s) {
+		LOG(L_ERR, "ERROR: textops: insert_header_lump(): not enough memory\n");
+		return -1;
+	}
+
+	memcpy(s, hname->s, hname->len);
+	s[hname->len] = ':';
+	s[hname->len+1] = ' ';
+	memcpy(s+hname->len+2, val->s, val->len);
+	s[hname->len+2+val->len] = '\r';
+	s[hname->len+2+val->len+1] = '\n';
+
+	if ( (lump_before?insert_new_lump_before(anchor, s, len, 0):insert_new_lump_after(anchor, s, len, 0)) == 0) {
+		LOG(L_ERR, "ERROR: textops: insert_header_lump(): Can't insert lump\n");
+		pkg_free(s);
+		return -1;
+	}
+	return 1;
+}
+
+static int insert_value_lump(struct sip_msg* msg, struct hdr_field* hf, char* msg_position, int lump_before, str *val) {
+	struct lump* anchor;
+	char *s;
+	int len;
+
+	anchor = anchor_lump(msg, msg_position - msg->buf, 0, 0);
+	if (anchor == 0) {
+		LOG(L_ERR, "ERROR: textops: insert_value_lump(): Can't get anchor\n");
+		return -1;
+	}
+
+	len=val->len+1;
+
+	s = (char*)pkg_malloc(len);
+	if (!s) {
+		LOG(L_ERR, "ERROR: textops: insert_value_lump(): not enough memory\n");
+		return -1;
+	}
+
+	if (!hf) {
+		memcpy(s, val->s, val->len);
+		len--;
+	}
+	else if (msg_position == hf->body.s+hf->body.len) {
+		s[0] = ',';
+		memcpy(s+1, val->s, val->len);
+	}
+	else {
+		memcpy(s, val->s, val->len);
+		s[val->len] = ',';
+	}
+	if ( (lump_before?insert_new_lump_before(anchor, s, len, 0):insert_new_lump_after(anchor, s, len, 0)) == 0) {
+		LOG(L_ERR, "ERROR: textops: insert_value_lump(): Can't insert lump\n");
+		pkg_free(s);
+		return -1;
+	}
+	return 1;
+}
+
+static int delete_value_lump(struct sip_msg* msg, struct hdr_field* hf, str *val) {
+	struct lump* l;
+	/* TODO: check already existing lumps */
+	if (hf && val->s == hf->body.s && val->len == hf->body.len) 	/* check if remove whole haeder? */
+		l=del_lump(msg, hf->name.s-msg->buf, hf->len, 0);
+	else
+		l=del_lump(msg, val->s-msg->buf, val->len, 0);
+	if (l==0) {
+		LOG(L_ERR, "ERROR: textops: delete_value_lump: not enough memory\n");
+		return -1;
+	}
+	return 1;
+}
+
+static int incexc_hf_value_str_f(struct sip_msg* msg, char* _hname, str* _pval) {
+	struct hname_data* hname = (void*) _hname;
+	struct hdr_field* hf, *lump_hf;
+	str val, hval1, hval2;
+	char *p;
+	int res;
+
+	val = *_pval;
+	if (!val.len) return -1;
+	hf = 0;
+	lump_hf = 0;
+	while (1) {
+		if (find_next_hf(msg, hname, &hf) < 0) return -1;
+		if (!hf) break;
+		hval2.len = 0;
+		p = hf->body.s;
+		do {
+			res = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2);
+			if (hval1.len && val.len == hval1.len && strncasecmp(val.s, hval1.s, val.len) == 0) {
+				switch (hname->oper) {
+					case hnoIsIncluded:
+					case hnoInclude:
+						return 1;
+					case hnoExclude:
+						adjust_lump_val_for_delete(hf, &hval2);
+						delete_value_lump(msg, hf, &hval2);
+					default:
+						break;
+				}
+			}
+		} while (res);
+		switch (hname->oper) {
+			case hnoInclude:
+				if (!lump_hf) {
+					lump_hf = hf;
+				}
+				break;
+			default:
+				break;
+		}
+	}
+	switch (hname->oper) {
+		case hnoIsIncluded:
+			return -1;
+		case hnoInclude:
+			if (lump_hf)
+				return insert_value_lump(msg, lump_hf, lump_hf->body.s+lump_hf->body.len, 1, &val);
+			else
+				return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val);
+		default:
+			return 1;
+	}
+}
+
+static int incexc_hf_value_f(struct sip_msg* msg, char* _hname, char* _val)
+{
+	str val;
+	int res;
+	
+	res = eval_hvalue_param(msg, (void*) _val, &val);
+	
+	if (res < 0) return res;
+	if (!val.len) return -1;
+
+	return incexc_hf_value_str_f(msg, _hname, &val);
+}
+
+#define INCEXC_HF_VALUE_FIXUP(_func,_oper) \
+static int _func (void** param, int param_no) {\
+	char* p = *param; \
+	int res=fixup_hname_str(param, param_no); \
+	if (res < 0) return res; \
+	if (param_no == 1) {\
+		if ( ((struct hname_data*)*param)->flags & HNF_IDX || ((struct hname_data*)*param)->param.len ) { \
+			LOG(L_ERR, "ERROR: textops: neither index nor param may be specified in '%s'\n", p);\
+			return E_CFG;\
+		}\
+		((struct hname_data*)*param)->oper = _oper;\
+	}\
+	return 0;\
+}
+
+INCEXC_HF_VALUE_FIXUP(include_hf_value_fixup, hnoInclude)
+INCEXC_HF_VALUE_FIXUP(exclude_hf_value_fixup, hnoExclude)
+INCEXC_HF_VALUE_FIXUP(hf_value_exists_fixup, hnoIsIncluded)
+
+static void get_uri_and_skip_until_params(str *param_area, str *name, str *uri) {
+	int i, quoted, uri_pos, uri_done;
+
+	name->len = 0;
+	uri->len = 0;
+	uri_done = 0;
+        name->s = param_area->s;
+	for (i=0; i<param_area->len && param_area->s[i]!=';'; ) {	/* [ *(token LSW)/quoted-string ] "<" addr-spec ">" | addr-spec */
+		/* skip name */
+		for (quoted=0, uri_pos=i; i<param_area->len; i++) {
+			if (!quoted) {
+				if (param_area->s[i] == '\"') {
+					quoted = 1;
+					uri_pos = -1;
+				}
+				else if (param_area->s[i] == '<' || param_area->s[i] == ';' || is_space(param_area->s[i])) break;
+			}
+			else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0;
+		}
+		if (!name->len)
+			name->len = param_area->s+i-name->s;
+		if (uri_pos >= 0 && !uri_done) {
+			uri->s = param_area->s+uri_pos;
+			uri->len = param_area->s+i-uri->s;
+		}
+		/* skip uri */
+		while (i<param_area->len && is_space(param_area->s[i])) i++;
+		if (i<param_area->len && param_area->s[i]=='<') {
+			uri->s = param_area->s+i;
+			uri->len = 0;
+			for (quoted=0; i<param_area->len; i++) {
+				if (!quoted) {
+					if (param_area->s[i] == '\"') quoted = 1;
+					else if (param_area->s[i] == '>') {
+						uri->len = param_area->s+i-uri->s+1;
+						uri_done = 1;
+						break;
+					}
+				}
+				else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0;
+			}
+		}
+	}
+        param_area->s+= i;
+	param_area->len-= i;
+	if (uri->s == name->s)
+		name->len = 0;
+}
+
+static int assign_hf_do_lumping(struct sip_msg* msg,struct hdr_field* hf, struct hname_data* hname, str* value, int upd_del_fl, str* lump_upd, str* lump_del, char delim) {
+	int len, i;
+	char *s;
+	struct lump* anchor;
+
+	if (upd_del_fl) {
+		len = value?lump_upd->len:lump_del->len;
+		if (len > 0) {
+			if (!del_lump(msg, (value?lump_upd->s:lump_del->s)-msg->buf, len, 0)) {
+				LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n");
+				return -1;
+			}
+		}
+		if (value && value->len) {
+			anchor = anchor_lump(msg, lump_upd->s - msg->buf, 0, 0);
+			if (anchor == 0) {
+				LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't get anchor\n");
+				return -1;
+			}
+
+			len = 1+value->len;
+			s = pkg_malloc(len);
+			if (!s) {
+				LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n");
+				return -1;
+			}
+			s[0]='=';
+			memcpy(s+1, value->s, value->len);
+			if ( (insert_new_lump_before(anchor, s, len, 0)) == 0) {
+				LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't insert lump\n");
+				pkg_free(s);
+				return -1;
+			}
+		}
+	}
+	else {
+		if (!value) return -1;
+
+		anchor = anchor_lump(msg, lump_del->s - msg->buf, 0, 0);
+		if (anchor == 0) {
+			LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't get anchor\n");
+			return -1;
+		}
+
+		len = 1+hname->param.len+(value->len?value->len+1:0);
+		s = pkg_malloc(len);
+		if (!s) {
+			LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: not enough memory\n");
+			return -1;
+		}
+		if (delim) {
+			s[0] = delim;
+			i = 1;
+		}
+		else {
+			i = 0;
+			len--;
+		}
+		memcpy(s+i, hname->param.s, hname->param.len);
+		if (value->len) {
+			s[hname->param.len+i]='=';
+			memcpy(s+i+hname->param.len+1, value->s, value->len);
+		}
+
+		if ( (insert_new_lump_before(anchor, s, len, 0)) == 0) {
+			LOG(L_ERR, "ERROR: textops: assign_hf_do_lumping: Can't insert lump\n");
+			pkg_free(s);
+			return -1;
+		}
+	}
+	return 1;
+}
+
+
+static int assign_hf_process_params(struct sip_msg* msg, struct hdr_field* hf, struct hname_data* hname, str* value, str* value_area) {
+	int r, r2, res=0;
+	str param_area, lump_upd, lump_del, dummy_val, dummy_name, dummy_uri;
+	param_area = *value_area;
+	get_uri_and_skip_until_params(&param_area, &dummy_name, &dummy_uri);
+	do {
+		r = find_hf_value_param(hname, &param_area, &dummy_val, &lump_upd, &lump_del);
+		r2 = assign_hf_do_lumping(msg, hf, hname, value, r, &lump_upd, &lump_del, ';');
+		if (res == 0)
+			res = r2;
+		if (r && !value) {   /* remove all parameters */
+			param_area.len -= lump_del.s+lump_del.len-param_area.s;
+			param_area.s = lump_del.s+lump_del.len;
+		}
+	} while (!value && r);
+	return res;
+}
+
+static int assign_hf_process2_params(struct sip_msg* msg, struct hdr_field* hf, struct hname_data* hname, str* value) {
+	int r, r2, res = 0;
+	str param_area, lump_upd, lump_del, dummy_val;
+	char delim;
+
+	param_area = hf->body;
+
+	do {
+		r = find_hf_value2_param(hname, &param_area, &dummy_val, &lump_upd, &lump_del, &delim);
+		r2 = assign_hf_do_lumping(msg, hf, hname, value, r, &lump_upd, &lump_del, delim);
+		if (res == 0)
+			res = r2;
+		if (r && !value) {   /* remove all parameters */
+			param_area.len -= lump_del.s+lump_del.len-param_area.s;
+			param_area.s = lump_del.s+lump_del.len;
+		}
+	} while (!value && r);
+	return res;
+
+}
+
+static int insupddel_hf_value_f(struct sip_msg* msg, char* _hname, char* _val) {
+	struct hname_data* hname = (void*) _hname;
+	struct hdr_field* hf;
+	str val, hval1, hval2;
+	int res;
+
+	if (_val) {
+		res = eval_hvalue_param(msg, (void*) _val, &val);
+		if (res < 0) return res;
+	}
+	switch (hname->oper) {
+		case hnoAppend:
+			if ((hname->flags & HNF_IDX) == 0) {
+				if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
+					LOG(L_ERR, "ERROR: textops: Error while parsing message\n");
+					return -1;
+				}
+				return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val);
+			}
+			else {
+				res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
+				if (res < 0) return res;
+				if (hf) {
+					return insert_value_lump(msg, hf, hval2.s+hval2.len,
+							res /* insert after, except it is last value in header */, &val);
+				}
+				else {
+					return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val);
+				}
+			}
+		case hnoInsert:
+			/* if !HNF_IDX is possible parse only until first hname header
+			 * but not trivial for HDR_OTHER_T header, not implemented */
+			res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
+			if (res < 0) return res;
+			if (hf && (hname->flags & HNF_IDX) == 0) {
+				return insert_header_lump(msg, hf->name.s, 1, &hname->hname, &val);
+			}
+			else if (!hf && hname->idx == 1) {
+				return insert_header_lump(msg, msg->unparsed, 1, &hname->hname, &val);
+			}
+			else if (hf) {
+				return insert_value_lump(msg, hf, hval2.s, 1, &val);
+			}
+			else
+				return -1;
+
+		case hnoRemove:
+		case hnoAssign:
+			if (hname->flags & HNF_ALL) {
+				struct hdr_field* hf = 0;
+				int fl = -1;
+				do {
+					res = find_next_hf(msg, hname, &hf);
+					if (res < 0) return res;
+					if (hf) {
+						if (!hname->param.len) {
+							fl = 1;
+							delete_value_lump(msg, hf, &hf->body);
+						}
+						else {
+							char *p;
+							hval2.len = 0;
+							p = hf->body.s;
+							do {
+								res = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2);
+								if (assign_hf_process_params(msg, hf, hname, _val?&val:0, &hval1) > 0)
+									fl = 1;
+							} while (res);
+						}
+					}
+				} while (hf);
+				return fl;
+			}
+			else {
+				res = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
+				if (res < 0) return res;
+				if (hf) {
+					if (!hname->param.len) {
+						if (hname->oper == hnoRemove) {
+							adjust_lump_val_for_delete(hf, &hval2);
+							return delete_value_lump(msg, hf, &hval2);
+						}
+						else {
+							res = delete_value_lump(msg, 0 /* delete only value part */, &hval1);
+							if (res < 0) return res;
+							if (val.len) {
+								return insert_value_lump(msg, 0 /* do not add delims */, hval1.s, 1, &val);
+							}
+							return 1;
+						}
+					}
+					else {
+						return assign_hf_process_params(msg, hf, hname, _val?&val:0, &hval1);
+					}
+				}
+			}
+			break;
+		case hnoRemove2:
+		case hnoAssign2:
+			if (hname->flags & HNF_ALL) {
+				struct hdr_field* hf = 0;
+				int fl = -1;
+				do {
+					res = find_next_hf(msg, hname, &hf);
+					if (res < 0) return res;
+					if (hf) {
+						if (!hname->param.len) {  /* the same as hnoRemove/hnoAssign */
+							fl = 1;
+							delete_value_lump(msg, hf, &hf->body);
+						}
+						else {
+
+							if (assign_hf_process2_params(msg, hf, hname, _val?&val:0) > 0)
+								fl = 1;
+						}
+					}
+				} while (hf);
+				return fl;
+			}
+			else {
+				res = find_hf_value_idx(msg, hname, &hf, 0, 0);
+				if (res < 0) return res;
+				if (hf) {
+					if (!hname->param.len) {
+						if (hname->oper == hnoRemove2) {
+							return delete_value_lump(msg, hf, &hf->body);
+						}
+						else {
+							res = delete_value_lump(msg, 0 /* delete only value part */, &hf->body);
+							if (res < 0) return res;
+							if (val.len) {
+								return insert_value_lump(msg, 0 /* do not add delims */, hf->body.s, 1, &val);
+							}
+							return 1;
+						}
+					}
+					else {
+						return assign_hf_process2_params(msg, hf, hname, _val?&val:0);
+					}
+				}
+			}
+			break;
+	}
+	return -1;
+}
+
+static int append_hf_value_fixup(void** param, int param_no) {
+	int res=fixup_hname_str(param, param_no);
+	if (res < 0) return res;
+	if (param_no == 1) {
+		if ( ((struct hname_data*)*param)->flags & HNF_ALL ) {
+			LOG(L_ERR, "ERROR: textops: asterisk not supported\n");
+			return E_CFG;
+		} else if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) {
+			((struct hname_data*)*param)->idx = -1;
+		}
+		if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) {
+			LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
+			return E_CFG;
+		}
+		if ( ((struct hname_data*)*param)->param.len ) {
+			LOG(L_ERR, "ERROR: textops: param not supported\n");
+			return E_CFG;
+		}
+		((struct hname_data*)*param)->oper = hnoAppend;
+	}
+	return 0;
+}
+
+static int insert_hf_value_fixup(void** param, int param_no) {
+	int res=fixup_hname_str(param, param_no);
+	if (res < 0) return res;
+	if (param_no == 1) {
+		if ( ((struct hname_data*)*param)->flags & HNF_ALL ) {
+			LOG(L_ERR, "ERROR: textops: asterisk not supported\n");
+			return E_CFG;
+		} else if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) {
+			((struct hname_data*)*param)->idx = 1;
+		}
+		if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) {
+			LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
+			return E_CFG;
+		}
+		if ( ((struct hname_data*)*param)->param.len ) {
+			LOG(L_ERR, "ERROR: textops: param not supported\n");
+			return E_CFG;
+		}
+		((struct hname_data*)*param)->oper = hnoInsert;
+	}
+	return 0;
+}
+
+static int remove_hf_value_fixup(void** param, int param_no) {
+	int res=fixup_hname_str(param, param_no);
+	if (res < 0) return res;
+	if (param_no == 1) {
+		if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) {
+			((struct hname_data*)*param)->idx = 1;
+			((struct hname_data*)*param)->flags |= HNF_IDX;
+		}
+		if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) {
+			LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
+			return E_CFG;
+		}
+		((struct hname_data*)*param)->oper = hnoRemove;
+	}
+	return 0;
+}
+
+static int assign_hf_value_fixup(void** param, int param_no) {
+	int res=fixup_hname_str(param, param_no);
+	if (res < 0) return res;
+	if (param_no == 1) {
+		if ( (((struct hname_data*)*param)->flags & HNF_ALL) && !((struct hname_data*)*param)->param.len) {
+			LOG(L_ERR, "ERROR: textops: asterisk not supported without param\n");
+			return E_CFG;
+		} else if ( (((struct hname_data*)*param)->flags & HNF_IDX) == 0 || !((struct hname_data*)*param)->idx ) {
+			((struct hname_data*)*param)->idx = 1;
+			((struct hname_data*)*param)->flags |= HNF_IDX;
+		}
+		if (((struct hname_data*)*param)->idx < -MAX_HF_VALUE_STACK) {
+			LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
+			return E_CFG;
+		}
+		((struct hname_data*)*param)->oper = hnoAssign;
+	}
+	return 0;
+}
+
+static int remove_hf_value2_fixup(void** param, int param_no) {
+	int res=remove_hf_value_fixup(param, param_no);
+	if (res < 0) return res;
+	if (param_no == 1) {
+		((struct hname_data*)*param)->oper = hnoRemove2;
+	}
+	return 0;
+}
+
+static int assign_hf_value2_fixup(void** param, int param_no) {
+	int res=assign_hf_value_fixup(param, param_no);
+	if (res < 0) return res;
+	if (param_no == 1) {
+		((struct hname_data*)*param)->oper = hnoAssign2;
+	}
+	return 0;
+}
+
+
+/* select implementation */
+static int sel_hf_value(str* res, select_t* s, struct sip_msg* msg) {  /* dummy */
+	return 0;
+}
+
+#define _ALLOC_INC_SIZE 1024
+
+static int sel_hf_value_name(str* res, select_t* s, struct sip_msg* msg) {
+	struct hname_data* hname;
+	struct hdr_field* hf;
+	str val, hval1, hval2, huri, dummy_name;
+	int r;
+	if (!msg) {
+		struct hdr_field hdr;
+		char buf[50];
+		int i, n;
+
+		if (s->params[1].type == SEL_PARAM_STR) {
+			hname = pkg_malloc(sizeof(*hname));
+			if (!hname) return E_OUT_OF_MEM;
+			memset(hname, 0, sizeof(*hname));
+
+			for (i=s->params[1].v.s.len-1; i>0; i--) {
+				if (s->params[1].v.s.s[i]=='_')
+					s->params[1].v.s.s[i]='-';
+			}
+			i = snprintf(buf, sizeof(buf)-1, "%.*s: X\n", s->params[1].v.s.len, s->params[1].v.s.s);
+			buf[i] = 0;
+
+			hname->hname = s->params[1].v.s;
+			parse_hname2(buf, buf+i, &hdr);
+
+			if (hdr.type == HDR_ERROR_T) return E_CFG;
+			hname->htype = hdr.type;
+
+			s->params[1].v.p = hname;
+			s->params[1].type = SEL_PARAM_PTR;
+		}
+		else {
+			hname = s->params[1].v.p;
+		}
+		n = s->param_offset[select_level+1] - s->param_offset[select_level];  /* number of values before NESTED */
+		if (n > 2 && s->params[2].type == SEL_PARAM_INT) {
+			hname->idx = s->params[2].v.i;
+			hname->flags |= HNF_IDX;
+			if (hname->idx < -MAX_HF_VALUE_STACK) {
+				LOG(L_ERR, "ERROR: textops: index cannot be lower than %d\n", -MAX_HF_VALUE_STACK);
+				return E_CFG;
+			}
+			if (hname->idx == 0)
+				hname->idx = 1;
+			i = 3;
+		}
+		else {
+			i = 2;
+			hname->idx = 1;
+		}
+		if (n > i && s->params[i].type == SEL_PARAM_STR) {
+			hname->param = s->params[i].v.s;
+			for (i=hname->param.len-1; i>0; i--) {
+				if (hname->param.s[i]=='_')
+					hname->param.s[i]='-';
+			}
+
+		}
+		s->params[1].v.p = hname;
+		s->params[1].type = SEL_PARAM_PTR;
+		hname->oper = hnoGetValue;
+
+		return 0;
+	}
+
+	res->len = 0;
+	res->s = 0;
+	hname = s->params[1].v.p;
+
+	switch (hname->oper) {
+		case hnoGetValueUri:
+			if (hname->flags & HNF_ALL || (hname->flags & HNF_IDX) == 0) {
+				char *buf = NULL;
+				int buf_len = 0;
+				
+				hf = 0;
+				do {
+					r = find_next_hf(msg, hname, &hf);
+					if (r < 0) break;
+					if (hf) {
+						char *p;
+						str huri;
+						hval2.len = 0;
+						p = hf->body.s;
+						do {
+							r = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2);
+							get_uri_and_skip_until_params(&hval1, &dummy_name, &huri);
+							if (huri.len) {
+							/* TODO: normalize uri, lowercase except quoted params, add/strip < > */
+								if (*huri.s == '<') {
+									huri.s++;
+									huri.len -= 2;
+								}
+							}							
+							if (res->len == 0) {  
+								*res = huri; /* first value, if is also last value then we don't need any buffer */
+							}
+							else {
+								if (buf) {
+									if (res->len+huri.len+1 > buf_len) {
+										buf_len = res->len+huri.len+1+_ALLOC_INC_SIZE;
+										res->s = pkg_realloc(buf, buf_len);
+										if (!res->s) {
+											pkg_free(buf);
+											LOG(L_ERR, "ERROR: textops: cannot realloc buffer\n");
+											res->len = 0;
+											return E_OUT_OF_MEM;
+										}
+										buf = res->s;
+									}
+								}
+								else {
+									/* 2nd value */
+									buf_len = res->len+huri.len+1+_ALLOC_INC_SIZE;
+									buf = pkg_malloc(buf_len);
+									if (!buf) { 
+										LOG(L_ERR, "ERROR: testops: out of memory\n");
+										res->len = 0;
+										return E_OUT_OF_MEM;
+									}
+									/* copy 1st value */
+									memcpy(buf, res->s, res->len);								
+									res->s = buf;
+								}
+								res->s[res->len] = ',';
+								res->len++;
+								if (huri.len) {
+									memcpy(res->s+res->len, huri.s, huri.len);
+									res->len += huri.len;
+								}
+							}
+						
+						} while (r);
+					}
+				} while (hf);
+				if (buf) {
+					res->s = get_static_buffer(res->len);
+					if (!res->s) {
+						pkg_free(buf);
+						res->len = 0;
+						LOG(L_ERR, "ERROR: testops: cannot allocate static buffer\n");
+						return E_OUT_OF_MEM;
+					}
+					memcpy(res->s, buf, res->len);
+					pkg_free(buf);
+				}
+			}
+			else {
+				r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
+				if (r > 0) {
+					get_uri_and_skip_until_params(&hval1, &dummy_name, res);
+					if (res->len && *res->s == '<') {
+						res->s++;   	/* strip < & > */
+						res->len-=2;
+					}
+				}
+			}
+			break;
+		case hnoGetValueName:
+			if ((hname->flags & HNF_ALL) == 0) {
+				r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
+				if (r > 0) {
+					get_uri_and_skip_until_params(&hval1, res, &dummy_name);
+					if (res->len >= 2 && res->s[0] == '\"' && res->s[res->len-1]=='\"' ) {
+						res->s++;   	/* strip quotes */
+						res->len-=2;
+					}
+				}
+			}
+			break;
+		case hnoGetValue:
+			if (hname->flags & HNF_ALL || (hname->flags & HNF_IDX) == 0) {
+				char *buf = NULL;
+				int buf_len = 0;
+
+				hf = 0;
+				do {
+					r = find_next_hf(msg, hname, &hf);
+					
+					if (r < 0) break;
+					if (hf) {
+						char *p;
+						hval2.len = 0;
+						p = hf->body.s;
+						do {
+							r = find_next_value(&p, hf->body.s+hf->body.len, &hval1, &hval2);
+							if (res->len == 0) {  
+								*res = hval1; /* first value, if is also last value then we don't need any buffer */
+							}
+							else {
+								if (buf) {
+									if (res->len+hval1.len+1 > buf_len) {
+										buf_len = res->len+hval1.len+1+_ALLOC_INC_SIZE;
+										res->s = pkg_realloc(buf, buf_len);
+										if (!res->s) {
+											pkg_free(buf);
+											LOG(L_ERR, "ERROR: textops: cannot realloc buffer\n");
+											res->len = 0;
+											return E_OUT_OF_MEM;
+										}
+										buf = res->s;
+									}
+								}
+								else {
+									/* 2nd value */
+									buf_len = res->len+hval1.len+1+_ALLOC_INC_SIZE;
+									buf = pkg_malloc(buf_len);
+									if (!buf) { 
+										LOG(L_ERR, "ERROR: testops: out of memory\n");
+										res->len = 0;
+										return E_OUT_OF_MEM;
+									}
+									/* copy 1st value */
+									memcpy(buf, res->s, res->len);								
+									res->s = buf;
+								}
+								res->s[res->len] = ',';
+								res->len++;
+								if (hval1.len) {
+									memcpy(res->s+res->len, hval1.s, hval1.len);
+									res->len += hval1.len;
+								}
+							}
+						} while (r);
+					}
+				} while (hf);
+				if (buf) {
+					res->s = get_static_buffer(res->len);
+					if (!res->s) {
+						pkg_free(buf);
+						res->len = 0;
+						LOG(L_ERR, "ERROR: testops: cannot allocate static buffer\n");
+						return E_OUT_OF_MEM;
+					}
+					memcpy(res->s, buf, res->len);
+					pkg_free(buf);
+				}
+			}
+			else {
+				r = find_hf_value_idx(msg, hname, &hf, &hval1, &hval2);
+				if (r > 0) {
+					if (hname->param.len) {
+						str d1, d2;
+						get_uri_and_skip_until_params(&hval1, &dummy_name, &huri);
+						if (find_hf_value_param(hname, &hval1, &val, &d1, &d2)) {
+							*res = val;
+						}
+					}
+					else {
+						*res = hval1;
+					}
+				}
+			}
+			break;
+		case hnoGetValue2:
+			r = find_hf_value_idx(msg, hname, &hf, 0, 0);
+			if (r > 0) {
+				if (hname->param.len) {
+					str d1, d2;
+					char c;
+					if (find_hf_value2_param(hname, &hf->body, &val, &d1, &d2, &c)) {
+						*res = val;
+					}
+				}
+				else {
+					*res = hf->body;
+				}
+			}
+			break;
+		default:
+			break;
+	}
+	return 0;
+}
+
+static int sel_hf_value_name_param_name(str* res, select_t* s, struct sip_msg* msg) {
+	return sel_hf_value_name(res, s, msg);
+}
+
+static int sel_hf_value_name_param_name2(str* res, select_t* s, struct sip_msg* msg) {
+	if (!msg) { /* eliminate "param" level */
+		int n;
+		n = s->param_offset[select_level+1] - s->param_offset[select_level];
+		s->params[n-2] = s->params[n-1];
+	}
+	return sel_hf_value_name(res, s, msg);
+}
+
+static int sel_hf_value_name_uri(str* res, select_t* s, struct sip_msg* msg) {
+	int r;
+	r = sel_hf_value_name(res, s, msg);
+	if (!msg && r==0) {
+		((struct hname_data*) s->params[1].v.p)->oper = hnoGetValueUri;
+	}
+	return r;
+}
+
+static int sel_hf_value_name_name(str* res, select_t* s, struct sip_msg* msg) {
+	int r;
+	r = sel_hf_value_name(res, s, msg);
+	if (!msg && r==0) {
+		((struct hname_data*) s->params[1].v.p)->oper = hnoGetValueName;
+	}
+	return r;
+}
+
+static int sel_hf_value_exists(str* res, select_t* s, struct sip_msg* msg) {  /* dummy */
+	return 0;
+}
+
+static int sel_hf_value_exists_param(str* res, select_t* s, struct sip_msg* msg) {
+	static char ret_val[] = "01";
+    int r;
+
+	if (!msg) {
+		r = sel_hf_value_name(res, s, msg);
+		if (r == 0)
+			((struct hname_data*) s->params[1].v.p)->oper = hnoIsIncluded;
+		return r;
+	}
+	r = incexc_hf_value_str_f(msg, s->params[1].v.p, &s->params[2].v.s);
+	res->s = &ret_val[r > 0];
+	res->len = 1;
+
+	return 0;
+}
+
+static int sel_hf_value2(str* res, select_t* s, struct sip_msg* msg) {  /* dummy */
+	return 0;
+}
+
+static int sel_hf_value2_name(str* res, select_t* s, struct sip_msg* msg) {
+	int r;
+	r = sel_hf_value_name(res, s, msg);
+	if (!msg && r==0) {
+		((struct hname_data*) s->params[1].v.p)->oper = hnoGetValue2;
+	}
+	return r;
+}
+
+static int sel_hf_value2_name_param_name(str* res, select_t* s, struct sip_msg* msg) {
+	return sel_hf_value2_name(res, s, msg);
+}
+
+SELECT_F(select_any_nameaddr)
+SELECT_F(select_any_uri)
+SELECT_F(select_anyheader_params)
+
+select_row_t sel_declaration[] = {
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value"), sel_hf_value, SEL_PARAM_EXPECTED},
+
+	{ sel_hf_value, SEL_PARAM_STR, STR_NULL, sel_hf_value_name, CONSUME_NEXT_INT | OPTIONAL | FIXUP_CALL},
+	{ sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("param"), sel_hf_value_name_param_name2, CONSUME_NEXT_STR | FIXUP_CALL},
+	{ sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("p"), sel_hf_value_name_param_name2, CONSUME_NEXT_STR | FIXUP_CALL},
+	{ sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("uri"), sel_hf_value_name_uri, FIXUP_CALL},
+	{ sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("name"), sel_hf_value_name_name, FIXUP_CALL},
+	{ sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED | CONSUME_NEXT_STR}, /* it duplicates param,p,name,... */
+	{ sel_hf_value_name, SEL_PARAM_STR, STR_STATIC_INIT("params"), select_anyheader_params, NESTED},
+
+	{ sel_hf_value_name_uri, SEL_PARAM_INT, STR_NULL, select_any_uri, NESTED},
+	{ sel_hf_value_name, SEL_PARAM_STR, STR_NULL, sel_hf_value_name_param_name, FIXUP_CALL},
+
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value_exists"), sel_hf_value_exists, CONSUME_NEXT_STR | SEL_PARAM_EXPECTED},
+	{ sel_hf_value_exists, SEL_PARAM_STR, STR_NULL, sel_hf_value_exists_param, FIXUP_CALL},
+
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("hf_value2"), sel_hf_value2, SEL_PARAM_EXPECTED},
+	{ sel_hf_value2, SEL_PARAM_STR, STR_NULL, sel_hf_value2_name, CONSUME_NEXT_INT | OPTIONAL | FIXUP_CALL},
+	{ sel_hf_value2_name, SEL_PARAM_STR, STR_STATIC_INIT("params"), select_anyheader_params, NESTED},
+	{ sel_hf_value2_name, SEL_PARAM_STR, STR_NULL, sel_hf_value2_name_param_name, FIXUP_CALL},
+	{ sel_hf_value2_name_param_name, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED},
+	{ sel_hf_value2_name_param_name, SEL_PARAM_STR, STR_STATIC_INIT("uri"), select_any_uri, NESTED},
+
+	{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
+};

+ 67 - 11
modules_k/dialog/dlg_profile.c

@@ -714,9 +714,23 @@ int	is_known_dlg(struct sip_msg *msg) {
 int	dlg_set_timeout_by_profile(struct dlg_profile_table *profile, 
 				   str *value, int timeout) 
 {
-	unsigned int		i;
+	unsigned int		i = 0;
+	dlg_cell_t		*this_dlg = NULL;
 	struct dlg_profile_hash	*ph = NULL;
 
+	/* Private structure necessary for manipulating dialog 
+         * timeouts outside of profile locks.  Admittedly, an
+         * ugly hack, but avoids some concurrency issues.
+         */
+
+	struct dlg_map_list {
+		unsigned int		h_id;
+		unsigned int		h_entry;
+		struct dlg_map_list	*next;
+	} *map_head, *map_scan, *map_scan_next;
+
+	map_head = NULL;
+
 	/* If the profile has no value, iterate through every 
 	 * node and set its timeout.
 	 */
@@ -730,11 +744,23 @@ int	dlg_set_timeout_by_profile(struct dlg_profile_table *profile,
 			if(!ph) continue;
 			
 			do { 
-				if(update_dlg_timeout(ph->dlg, timeout) < 0) {
-					lock_release(&profile->lock);
+				struct dlg_map_list *d = malloc(sizeof(struct dlg_map_list));
+
+				if(!d)
 					return -1;
-				}
 
+				memset(d, 0, sizeof(struct dlg_map_list));
+
+				d->h_id = ph->dlg->h_id;
+				d->h_entry = ph->dlg->h_entry;
+
+				if(map_head == NULL)
+					map_head = d;
+				else {
+					d->next = map_head;
+					map_head = d;
+				}
+	
 				ph = ph->next;
 			} while(ph != profile->entries[i].first);
 		} 
@@ -750,15 +776,24 @@ int	dlg_set_timeout_by_profile(struct dlg_profile_table *profile,
 		ph = profile->entries[i].first;
 
 		if(ph) {
-			do { 
-				if(value->len == ph->value.len &&
-				   memcmp(value->s, ph->value.s, 
-					  value->len) == 0) {
+			do {
+				if(ph && value->len == ph->value.len &&
+				   memcmp(value->s, ph->value.s, value->len) == 0) {
+					struct dlg_map_list *d = malloc(sizeof(struct dlg_map_list));
 
-					if(update_dlg_timeout(ph->dlg, 
-							timeout) < 0) {
-						lock_release(&profile->lock);
+					if(!d)
 						return -1;
+
+					memset(d, 0, sizeof(struct dlg_map_list));
+
+					d->h_id = ph->dlg->h_id;
+					d->h_entry = ph->dlg->h_entry;
+
+					if(map_head == NULL)
+						map_head = d;
+					else {
+						d->next = map_head;
+						map_head = d;
 					}
 				}
 
@@ -769,6 +804,27 @@ int	dlg_set_timeout_by_profile(struct dlg_profile_table *profile,
 		lock_release(&profile->lock);
 	}
 
+	/* Walk the list and bulk-set the timeout */
+	
+	for(map_scan = map_head; map_scan != NULL; map_scan = map_scan_next) {
+		map_scan_next = map_scan->next;
+
+		this_dlg = dlg_lookup(map_scan->h_entry, map_scan->h_id);
+
+		if(!this_dlg) {
+			LM_CRIT("Unable to find dialog %d:%d\n", map_scan->h_entry, map_scan->h_id);
+		} else if(this_dlg->state >= DLG_STATE_EARLY) {	
+			if(update_dlg_timeout(this_dlg, timeout) < 0) {
+               			LM_ERR("Unable to set timeout on %d:%d\n", map_scan->h_entry,
+					map_scan->h_id);
+			}
+
+	                dlg_release(this_dlg);
+		}
+
+		free(map_scan);
+	}
+
 	return 0;
 }
 

+ 1 - 1
modules_k/domain/api.h

@@ -32,7 +32,7 @@ typedef struct domain_api {
 } domain_api_t;
 
 typedef int (*bind_domain_t)(domain_api_t* api);
-int bind_domain(domain_api_t* api);
+extern int bind_domain(domain_api_t* api);
 
 
 #endif

+ 1 - 1
modules_k/domain/domain_mod.c

@@ -128,7 +128,7 @@ static cmd_export_t cmds[] = {
 	{"lookup_domain", (cmd_function)w_lookup_domain, 2, fixup_pvar_str,
 	 fixup_free_pvar_str,
 	 REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
-	{"bind_domain", (cmd_function)bind_domain, 0, 0, 0, 0},
+	{"bind_domain", (cmd_function)bind_domain, 1, 0, 0, 0},
 	{0, 0, 0, 0, 0, 0}
 };
 

+ 0 - 1
modules_k/htable/htable.c

@@ -554,7 +554,6 @@ static void htable_rpc_get(rpc_t* rpc, void* c) {
 	ht_t *ht;
 	ht_cell_t *htc;	/*!< One HT cell */
 	void* th;
-	void* ih;
 	void* vh;
 
 	if (rpc->scan(c, "SS", &htname, &keyname) < 2) {

+ 36 - 8
modules_k/nathelper/README

@@ -78,6 +78,10 @@ Ovidiu Sas
 
               7.1. nh_enable_ping
 
+        8. Selects
+
+              8.1. @nathelper.rewrite_contact[n]
+
    2. Frequently Asked Questions
 
    List of Examples
@@ -102,6 +106,7 @@ Ovidiu Sas
    1.18. $rr_count usage
    1.19. $rr_top_count usage
    1.20. nh_enable_ping usage
+   1.21. @nathelper.rewrite_contact usage
 
 Chapter 1. Admin Guide
 
@@ -148,6 +153,10 @@ Chapter 1. Admin Guide
 
         7.1. nh_enable_ping
 
+   8. Selects
+
+        8.1. @nathelper.rewrite_contact[n]
+
 1. Overview
 
    This is a module to help with NAT traversal and reuse of tcp
@@ -396,7 +405,7 @@ modparam("nathelper", "keepalive_timeout", 120)
    5.7. add_contact_alias([ip_addr, port, proto])
    5.8. handle_ruri_alias()
 
-5.1.  fix_nated_contact()
+5.1. fix_nated_contact()
 
    Rewrites Contact HF to contain request's source address:port.
 
@@ -408,7 +417,7 @@ modparam("nathelper", "keepalive_timeout", 120)
 if (search("User-Agent: Cisco ATA.*") {fix_nated_contact();};
 ...
 
-5.2.  fix_nated_sdp(flags [, ip_address])
+5.2. fix_nated_sdp(flags [, ip_address])
 
    Alters the SDP information in orer to facilitate NAT traversal. What
    changes to be performed may be controled via the "flags" parameter.
@@ -436,7 +445,7 @@ if (search("User-Agent: Cisco ATA.*") {fix_nated_contact();};
 if (search("User-Agent: Cisco ATA.*") {fix_nated_sdp("3");};
 ...
 
-5.3.  add_rcv_param([flag]),
+5.3. add_rcv_param([flag]),
 
    Add received parameter to Contact header fields or Contact URI. The
    parameter will contain URI created from the source IP, port, and
@@ -460,7 +469,7 @@ add_rcv_param(); # add the parameter to the Contact header
 add_rcv_param("1"); # add the parameter to the Contact URI
 ...
 
-5.4.  fix_nated_register()
+5.4. fix_nated_register()
 
    The function creates a URI consisting of the source IP, port, and
    protocol and stores the URI in an Attribute-Value-Pair. The URI will be
@@ -478,7 +487,7 @@ add_rcv_param("1"); # add the parameter to the Contact URI
 fix_nated_register();
 ...
 
-5.5.  nat_uac_test(flags)
+5.5. nat_uac_test(flags)
 
    Tries to guess if client's request originated behind a nat. The
    parameter determines what heuristics is used.
@@ -494,6 +503,9 @@ fix_nated_register();
      * 32 - test if the source IP address of signaling is a RFC1918
        address
      * 64 - test if the source connection of signaling is a WebSocket
+     * 128 - test if the Contact URI port differs from the source port of
+       the request (Warning: this is might be legal or even intended
+       combination in non natted scenarios)
 
    All flags can be bitwise combined, the test returns true if any of the
    tests identified a NAT.
@@ -501,14 +513,14 @@ fix_nated_register();
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE, BRANCH_ROUTE.
 
-5.6.  is_rfc1918(ip_address)
+5.6. is_rfc1918(ip_address)
 
    Determines if the address in the parameter is an rfc1918 address. The
    parameter allows pseudo-variables usage.
 
    This function can be used from ANY_ROUTE.
 
-5.7.  add_contact_alias([ip_addr, port, proto])
+5.7. add_contact_alias([ip_addr, port, proto])
 
    Adds ;alias=ip~port~transport parameter to contact URI containing
    either received ip, port, and transport protocol or those given as
@@ -528,7 +540,7 @@ fix_nated_register();
     };
 ...
 
-5.8.  handle_ruri_alias()
+5.8. handle_ruri_alias()
 
    Checks if Request URI has alias param and if so, removes it and sets
    $du based on its value. Note that this means that routing of request is
@@ -607,6 +619,22 @@ fix_nated_register();
 $ kamctl fifo nh_enable_ping 1
 ...
 
+8. Selects
+
+   8.1. @nathelper.rewrite_contact[n]
+
+8.1. @nathelper.rewrite_contact[n]
+
+   Get n-th Contact value with IP:Port rewritten to source ip:port. N is
+   counted from 1. Only IP:port is rewritten, remaining part are left
+   unchanged. Full nameaddr is supported.
+
+   Example 1.21. @nathelper.rewrite_contact usage
+...
+$c = @nathelper.rewrite_contact[1];
+...
+$c2 = @nathelper.rewrite_contact[1].nameaddr.uri;
+
 Chapter 2. Frequently Asked Questions
 
    2.1. What happend with "rtpproxy_disable" parameter?

+ 27 - 0
modules_k/nathelper/doc/nathelper_admin.xml

@@ -570,6 +570,11 @@ fix_nated_register();
 			<emphasis>64</emphasis> -  test if the source connection of
 			signaling is a WebSocket
 			</para></listitem>
+			<listitem><para>
+			<emphasis>128</emphasis> -  test if the Contact URI port differs
+			from the source port of the request (Warning: this is might be legal
+			or even intended combination in non natted scenarios)
+			</para></listitem>
 			</itemizedlist>
 		<para>
 		All flags can be bitwise combined, the test returns true if any of 
@@ -736,5 +741,27 @@ $ &ctltool; fifo nh_enable_ping 1
 
 	</section>
 
+	<section>
+		<title>Selects</title>
+
+		<section id="nathelper.rewrite_contact">
+		<title>@nathelper.rewrite_contact[n]</title>
+		<para>
+			Get n-th Contact value with IP:Port rewritten to source ip:port. N is counted from 1.
+		    Only IP:port is rewritten, remaining part are left unchanged. Full nameaddr is supported.
+		</para>
+		<example>
+			<title>@nathelper.rewrite_contact usage</title>
+			<programlisting>
+...
+$c = @nathelper.rewrite_contact[1];
+...
+$c2 = @nathelper.rewrite_contact[1].nameaddr.uri;
+			</programlisting>
+		</example>
+		</section>
+
+	</section>
+
 </chapter>
 

+ 110 - 1
modules_k/nathelper/nathelper.c

@@ -220,6 +220,7 @@
 #include "../../socket_info.h"
 #include "../../mod_fix.h"
 #include "../../dset.h"
+#include "../../select.h"
 #include "../registrar/sip_msg.h"
 #include "../usrloc/usrloc.h"
 #include "nathelper.h"
@@ -243,6 +244,7 @@ MODULE_VERSION
 #define	NAT_UAC_TEST_RPORT	0x10
 #define	NAT_UAC_TEST_O_1918	0x20
 #define NAT_UAC_TEST_WS		0x40
+#define	NAT_UAC_TEST_C_PORT	0x80
 
 
 #define DEFAULT_RTPP_SET_ID		0
@@ -439,6 +441,27 @@ struct module_exports exports = {
 };
 
 
+static int
+sel_nathelper(str* res, select_t* s, struct sip_msg* msg)
+{
+
+	/* dummy */
+	return 0;
+}
+
+static int sel_rewrite_contact(str* res, select_t* s, struct sip_msg* msg);
+
+SELECT_F(select_any_nameaddr)
+
+select_row_t sel_declaration[] = {
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("nathelper"), sel_nathelper, SEL_PARAM_EXPECTED},
+	{ sel_nathelper, SEL_PARAM_STR, STR_STATIC_INIT("rewrite_contact"), sel_rewrite_contact, CONSUME_NEXT_INT },
+
+	{ sel_rewrite_contact, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED | CONSUME_NEXT_STR},
+
+	{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
+};
+
 static int
 fixup_fix_sdp(void** param, int param_no)
 {
@@ -689,6 +712,8 @@ mod_init(void)
 		nets_1918[i].netaddr = ntohl(addr.s_addr) & nets_1918[i].mask;
 	}
 
+	register_select_table(sel_declaration);
+
 	return 0;
 }
 
@@ -1372,6 +1397,27 @@ contact_1918(struct sip_msg* msg)
 	return (is1918addr(&(uri.host)) == 1) ? 1 : 0;
 }
 
+/*
+ * test if source port of signaling is different from
+ * port advertised in Contact
+ */
+static int
+contact_rport(struct sip_msg* msg)
+{
+	struct sip_uri uri;
+	contact_t* c;
+
+	if (get_contact_uri(msg, &uri, &c) == -1) {
+		return -1;
+	}
+
+	if (msg->rcv.src_port != (uri.port_no ? uri.port_no : SIP_PORT)) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
 /*
  * test for occurrence of RFC1918 IP address in SDP
  */
@@ -1472,12 +1518,19 @@ nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2)
 		return 1;
 
 	/*
- 	 * tests prototype to check whether the message arrived on a WebSocket
+	 * test prototype to check whether the message arrived on a WebSocket
  	 */
 	if ((tests & NAT_UAC_TEST_WS)
 		&& (msg->rcv.proto == PROTO_WS || msg->rcv.proto == PROTO_WSS))
 		return 1;
 
+	/*
+	 * test if source port of signaling is different from
+	 * port advertised in Contact
+	 */
+	if ((tests & NAT_UAC_TEST_C_PORT) && (contact_rport(msg) > 0))
+		return 1;
+
 	/* no test succeeded */
 	return -1;
 
@@ -2292,3 +2345,59 @@ done:
 	/* let the core handle further the reply */
 	return 1;
 }
+
+static int
+sel_rewrite_contact(str* res, select_t* s, struct sip_msg* msg)
+{
+	static char buf[500];
+	contact_t* c;
+	int n, def_port_fl, len;
+	char *cp;
+	str hostport;
+	struct sip_uri uri;
+
+	res->len = 0;
+	n = s->params[2].v.i;
+	if (n <= 0) {
+		LOG(L_ERR, "ERROR: rewrite_contact[%d]: zero or negative index not supported\n", n);
+		return -1;
+	}
+	c = 0;
+	do {
+		if (contact_iterator(&c, msg, c) < 0 || !c)
+			return -1;
+		n--;
+	} while (n > 0);
+
+	if (parse_uri(c->uri.s, c->uri.len, &uri) < 0 || uri.host.len <= 0) {
+		LOG(L_ERR, "rewrite_contact[%d]: Error while parsing Contact URI\n", s->params[2].v.i);
+		return -1;
+	}
+	len = c->len - uri.host.len;
+	if (uri.port.len > 0)
+		len -= uri.port.len;
+	def_port_fl = (msg->rcv.proto == PROTO_TLS && msg->rcv.src_port == SIPS_PORT) || (msg->rcv.proto != PROTO_TLS && msg->rcv.src_port == SIP_PORT);
+	if (!def_port_fl)
+		len += 1/*:*/+5/*port*/;
+	if (len > sizeof(buf)) {
+		LOG(L_ERR, "ERROR: rewrite_contact[%d]: contact too long\n", s->params[2].v.i);
+		return -1;
+	}
+	hostport = uri.host;
+	if (uri.port.len > 0)
+		hostport.len = uri.port.s + uri.port.len - uri.host.s;
+
+	res->s = buf;
+	res->len = hostport.s - c->name.s;
+	memcpy(buf, c->name.s, res->len);
+	cp = ip_addr2a(&msg->rcv.src_ip);
+	if (def_port_fl) {
+		res->len+= snprintf(buf+res->len, sizeof(buf)-res->len, "%s", cp);
+	} else {
+		res->len+= snprintf(buf+res->len, sizeof(buf)-res->len, "%s:%d", cp, msg->rcv.src_port);
+	}
+	memcpy(buf+res->len, hostport.s+hostport.len, c->len-(hostport.s+hostport.len-c->name.s));
+	res->len+= c->len-(hostport.s+hostport.len-c->name.s);
+
+	return 0;
+}

+ 16 - 0
modules_k/registrar/api.c

@@ -78,6 +78,21 @@ int regapi_lookup(struct sip_msg *msg, char *table)
 	return lookup(msg, d, NULL);
 }
 
+/**
+ *
+ */
+int regapi_lookup_uri(struct sip_msg *msg, char *table, str * uri)
+{
+	udomain_t* d;
+
+	if(ul.get_udomain(table, &d)<0)
+	{
+		LM_ERR("usrloc domain [%s] not found\n", table);
+		return -1;
+	}
+	return lookup(msg, d, uri);
+}
+
 /**
  *
  */
@@ -105,6 +120,7 @@ int bind_registrar(registrar_api_t* api)
 	api->save       = regapi_save;
 	api->save_uri   = regapi_save_uri;
 	api->lookup     = regapi_lookup;
+	api->lookup_uri = regapi_lookup_uri;
 	api->registered = regapi_registered;
 
 	return 0;

+ 4 - 0
modules_k/registrar/api.h

@@ -40,6 +40,9 @@ int regapi_save_uri(struct sip_msg *msg, char *table, int flags, str *uri);
 typedef int (*regapi_lookup_f)(struct sip_msg *msg, char *table);
 int regapi_lookup(struct sip_msg *msg, char *table);
 
+typedef int (*regapi_lookup_uri_f)(struct sip_msg *msg, char *table, str *uri);
+int regapi_lookup_uri(struct sip_msg *msg, char *table, str *uri);
+
 /**
  * @brief REGISTRAR API structure
  */
@@ -47,6 +50,7 @@ typedef struct registrar_api {
 	regapi_save_f       save;
 	regapi_save_uri_f   save_uri;
 	regapi_lookup_f     lookup;
+	regapi_lookup_uri_f lookup_uri;
 	regapi_lookup_f     registered;
 } registrar_api_t;
 

+ 0 - 0
modules_s/nathelper/Makefile → obsolete/nathelper/Makefile


+ 0 - 0
modules_s/nathelper/README → obsolete/nathelper/README


+ 0 - 0
modules_s/nathelper/TODO → obsolete/nathelper/TODO


+ 0 - 0
modules_s/nathelper/doc/Makefile → obsolete/nathelper/doc/Makefile


+ 0 - 0
modules_s/nathelper/doc/functions.xml → obsolete/nathelper/doc/functions.xml


+ 0 - 0
modules_s/nathelper/doc/nathelper.xml → obsolete/nathelper/doc/nathelper.xml


+ 0 - 0
modules_s/nathelper/doc/params.xml → obsolete/nathelper/doc/params.xml


+ 0 - 0
modules_s/nathelper/nathelper.c → obsolete/nathelper/nathelper.c


+ 0 - 0
modules_s/nathelper/nathelper.cfg → obsolete/nathelper/nathelper.cfg


+ 0 - 0
modules_s/nathelper/nathelper.h → obsolete/nathelper/nathelper.h


+ 0 - 0
modules_s/nathelper/natping.c → obsolete/nathelper/natping.c


+ 0 - 0
modules_s/nathelper/nhelpr_funcs.c → obsolete/nathelper/nhelpr_funcs.c


+ 0 - 0
modules_s/nathelper/nhelpr_funcs.h → obsolete/nathelper/nhelpr_funcs.h


+ 0 - 0
modules_s/permissions/Makefile → obsolete/permissions/Makefile


+ 0 - 0
modules_s/permissions/README → obsolete/permissions/README


+ 0 - 0
modules_s/permissions/allow_files.c → obsolete/permissions/allow_files.c


+ 0 - 0
modules_s/permissions/allow_files.h → obsolete/permissions/allow_files.h


+ 0 - 0
modules_s/permissions/config/permissions.allow → obsolete/permissions/config/permissions.allow


+ 0 - 0
modules_s/permissions/config/permissions.deny → obsolete/permissions/config/permissions.deny


+ 0 - 0
modules_s/permissions/config/register.allow → obsolete/permissions/config/register.allow


+ 0 - 0
modules_s/permissions/config/register.deny → obsolete/permissions/config/register.deny


+ 0 - 0
modules_s/permissions/doc/Makefile → obsolete/permissions/doc/Makefile


+ 0 - 0
modules_s/permissions/doc/functions.xml → obsolete/permissions/doc/functions.xml


+ 0 - 0
modules_s/permissions/doc/params.xml → obsolete/permissions/doc/params.xml


+ 0 - 0
modules_s/permissions/doc/permissions.xml → obsolete/permissions/doc/permissions.xml


+ 0 - 0
modules_s/permissions/doc/xmlrpc.xml → obsolete/permissions/doc/xmlrpc.xml


+ 0 - 0
modules_s/permissions/im_db.c → obsolete/permissions/im_db.c


+ 0 - 0
modules_s/permissions/im_db.h → obsolete/permissions/im_db.h


+ 0 - 0
modules_s/permissions/im_hash.c → obsolete/permissions/im_hash.c


+ 0 - 0
modules_s/permissions/im_hash.h → obsolete/permissions/im_hash.h


+ 0 - 0
modules_s/permissions/im_locks.c → obsolete/permissions/im_locks.c


+ 0 - 0
modules_s/permissions/im_locks.h → obsolete/permissions/im_locks.h


+ 0 - 0
modules_s/permissions/im_rpc.c → obsolete/permissions/im_rpc.c


+ 0 - 0
modules_s/permissions/im_rpc.h → obsolete/permissions/im_rpc.h


+ 0 - 0
modules_s/permissions/ip_set.c → obsolete/permissions/ip_set.c


+ 0 - 0
modules_s/permissions/ip_set.h → obsolete/permissions/ip_set.h


+ 0 - 0
modules_s/permissions/ip_set_rpc.c → obsolete/permissions/ip_set_rpc.c


+ 0 - 0
modules_s/permissions/ip_set_rpc.h → obsolete/permissions/ip_set_rpc.h


+ 0 - 0
modules_s/permissions/ip_tree.c → obsolete/permissions/ip_tree.c


+ 0 - 0
modules_s/permissions/ip_tree.h → obsolete/permissions/ip_tree.h


+ 0 - 0
modules_s/permissions/ipmatch.c → obsolete/permissions/ipmatch.c


+ 0 - 0
modules_s/permissions/ipmatch.h → obsolete/permissions/ipmatch.h


+ 0 - 0
modules_s/permissions/parse_config.c → obsolete/permissions/parse_config.c


+ 0 - 0
modules_s/permissions/parse_config.h → obsolete/permissions/parse_config.h


+ 0 - 0
modules_s/permissions/permissions.c → obsolete/permissions/permissions.c


+ 0 - 0
modules_s/permissions/permissions.h → obsolete/permissions/permissions.h


+ 0 - 0
modules_s/permissions/permissions_rpc.h → obsolete/permissions/permissions_rpc.h


+ 0 - 0
modules_s/permissions/rule.c → obsolete/permissions/rule.c


+ 0 - 0
modules_s/permissions/rule.h → obsolete/permissions/rule.h


+ 0 - 0
modules_s/permissions/trusted.c → obsolete/permissions/trusted.c


+ 0 - 0
modules_s/permissions/trusted.h → obsolete/permissions/trusted.h


+ 0 - 0
modules_s/permissions/trusted_hash.c → obsolete/permissions/trusted_hash.c


+ 0 - 0
modules_s/permissions/trusted_hash.h → obsolete/permissions/trusted_hash.h


+ 0 - 0
modules_s/permissions/trusted_rpc.c → obsolete/permissions/trusted_rpc.c


+ 0 - 0
modules_s/permissions/trusted_rpc.h → obsolete/permissions/trusted_rpc.h


+ 0 - 0
modules_s/textops/Makefile → obsolete/textops/Makefile


+ 0 - 0
modules_s/textops/README → obsolete/textops/README


+ 0 - 0
modules_s/textops/doc/Makefile → obsolete/textops/doc/Makefile


+ 0 - 0
modules_s/textops/doc/functions.xml → obsolete/textops/doc/functions.xml


+ 0 - 0
modules_s/textops/doc/params.xml → obsolete/textops/doc/params.xml


+ 0 - 0
modules_s/textops/doc/textops.xml → obsolete/textops/doc/textops.xml


+ 0 - 0
modules_s/textops/textops.c → obsolete/textops/textops.c


+ 23 - 4
pkg/kamailio/fedora/16/kamailio.spec

@@ -367,14 +367,14 @@ make every-module skip_modules="auth_identity db_cassandra iptrtpproxy \
 	kxml kperl ksnmpstats kxmpp kcarrierroute kberkeley kldap kutils \
 	kpurple ktls kwebsocket kpresence klua kpython kgeoip ksqlite kjson \
 	kredis kmono" \
-	include_modules="cdp mangler xhttp_pi"
+	include_modules="cdp mangler print_lib xhttp_pi"
 %else
 make every-module skip_modules="auth_identity db_cassandra iptrtpproxy\
 	db_oracle memcached mi_xmlrpc osp" \
 	group_include="kstandard kmysql kpostgres kcpl kunixodbc \
 	kxml kperl ksnmpstats kxmpp kberkeley kldap kutils \
 	kpurple ktls kwebsocket kpresence klua kpython ksqlite" \
-	include_modules="cdp mangler xhttp_pi"
+	include_modules="cdp mangler print_lib xhttp_pi"
 %endif
 make utils
 
@@ -391,7 +391,7 @@ make install-modules-all skip_modules="auth_identity db_cassandra iptrtpproxy\
 	kxml kperl ksnmpstats kxmpp kcarrierroute kberkeley kldap kutils\
 	kpurple ktls kwebsocket kpresence klua kpython kgeoip ksqlite kjson\
 	kredis kmono" \
-	include_modules="cdp mangler xhttp_pi"
+	include_modules="cdp mangler print_lib xhttp_pi"
 
 mkdir -p $RPM_BUILD_ROOT/%{_unitdir}
 install -m644 pkg/kamailio/fedora/%{?fedora}/kamailio.service \
@@ -406,7 +406,7 @@ make install-modules-all skip_modules="auth_identity db_cassandra iptrtpproxy\
 	group_include="kstandard kmysql kpostgres kcpl kunixodbc \
 	kxml kperl ksnmpstats kxmpp kberkeley kldap kutils \
 	kpurple ktls kwebsocket kpresence klua kpython ksqlite" \
-	include_modules="cdp mangler xhttp_pi"
+	include_modules="cdp mangler print_lib xhttp_pi"
 
 mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d
 install -m755 pkg/kamailio/centos/%{?centos}/kamailio.init \
@@ -496,6 +496,8 @@ fi
 %doc %{_docdir}/kamailio/modules/README.pdb
 %doc %{_docdir}/kamailio/modules/README.pipelimit
 %doc %{_docdir}/kamailio/modules/README.prefix_route
+%doc %{_docdir}/kamailio/modules/README.print
+%doc %{_docdir}/kamailio/modules/README.print_lib
 %doc %{_docdir}/kamailio/modules/README.ratelimit
 %doc %{_docdir}/kamailio/modules/README.rtpproxy
 %doc %{_docdir}/kamailio/modules/README.sanity
@@ -510,6 +512,11 @@ fi
 %doc %{_docdir}/kamailio/modules/README.tm
 %doc %{_docdir}/kamailio/modules/README.tmrec
 %doc %{_docdir}/kamailio/modules/README.topoh
+%doc %{_docdir}/kamailio/modules/README.uid_auth_db
+%doc %{_docdir}/kamailio/modules/README.uid_avp_db
+%doc %{_docdir}/kamailio/modules/README.uid_domain
+%doc %{_docdir}/kamailio/modules/README.uid_gflags
+%doc %{_docdir}/kamailio/modules/README.uid_uri_db
 %doc %{_docdir}/kamailio/modules/README.xhttp
 %doc %{_docdir}/kamailio/modules/README.xhttp_rpc
 %doc %{_docdir}/kamailio/modules/README.xlog
@@ -587,6 +594,9 @@ fi
 %{_libdir}/kamailio/libkmi.so
 %{_libdir}/kamailio/libkmi.so.1
 %{_libdir}/kamailio/libkmi.so.1.0
+%{_libdir}/kamailio/libprint.so
+%{_libdir}/kamailio/libprint.so.1
+%{_libdir}/kamailio/libprint.so.1.2
 %{_libdir}/kamailio/libsrdb1.so
 %{_libdir}/kamailio/libsrdb1.so.1
 %{_libdir}/kamailio/libsrdb1.so.1.0
@@ -627,6 +637,8 @@ fi
 %{_libdir}/kamailio/modules/pdb.so
 %{_libdir}/kamailio/modules/pipelimit.so
 %{_libdir}/kamailio/modules/prefix_route.so
+%{_libdir}/kamailio/modules/print.so
+%{_libdir}/kamailio/modules/print_lib.so
 %{_libdir}/kamailio/modules/ratelimit.so
 %{_libdir}/kamailio/modules/rtpproxy.so
 %{_libdir}/kamailio/modules/sanity.so
@@ -641,6 +653,11 @@ fi
 %{_libdir}/kamailio/modules/tm.so
 %{_libdir}/kamailio/modules/tmrec.so
 %{_libdir}/kamailio/modules/topoh.so
+%{_libdir}/kamailio/modules/uid_auth_db.so
+%{_libdir}/kamailio/modules/uid_avp_db.so
+%{_libdir}/kamailio/modules/uid_domain.so
+%{_libdir}/kamailio/modules/uid_gflags.so
+%{_libdir}/kamailio/modules/uid_uri_db.so
 %{_libdir}/kamailio/modules/xhttp.so
 %{_libdir}/kamailio/modules/xhttp_rpc.so
 %{_libdir}/kamailio/modules/xlog.so
@@ -1006,6 +1023,8 @@ fi
 %changelog
 * Fri Dec 21 2012 Peter Dunkley <[email protected]>
   - Added db2_ldap, db2_ops, and timer to the build
+  - Added uid_auth_db, uid_avp_db, uid_domain, uid_gflags, uid_uri_db, print,
+    and print_lib to the build
 * Thu Dec 13 2012 Peter Dunkley <[email protected]>
   - Added xhttp_pi framework examples to the installation
   - Added xhttp_pi README to the installation

+ 10 - 3
pvapi.c

@@ -1854,9 +1854,11 @@ void pv_destroy_api(void)
 static char **_pv_print_buffer = NULL;
 #define PV_DEFAULT_PRINT_BUFFER_SIZE 1024
 static int _pv_print_buffer_size  = PV_DEFAULT_PRINT_BUFFER_SIZE;
+static int _pv_print_buffer_size_active  = 0;
 /* 6 mod params + 4 direct usage from mods */
 #define PV_DEFAULT_PRINT_BUFFER_SLOTS 10
 static int _pv_print_buffer_slots = PV_DEFAULT_PRINT_BUFFER_SLOTS;
+static int _pv_print_buffer_slots_active = 0;
 static int _pv_print_buffer_index = 0;
 
 /**
@@ -1890,6 +1892,9 @@ int pv_init_buffer(void)
 	}
 	LM_DBG("PV print buffer initialized to [%d][%d]\n",
 			_pv_print_buffer_slots, _pv_print_buffer_size);
+	_pv_print_buffer_slots_active = _pv_print_buffer_slots;
+	_pv_print_buffer_size_active = _pv_print_buffer_size;
+
 	return 0;
 }
 
@@ -1902,12 +1907,14 @@ void pv_destroy_buffer(void)
 
 	if(_pv_print_buffer==NULL)
 		return;
-	for(i=0; i<_pv_print_buffer_slots; i++)
+	for(i=0; i<_pv_print_buffer_slots_active; i++)
 	{
 		if(_pv_print_buffer[i]!=NULL)
 			pkg_free(_pv_print_buffer[i]);
 	}
 	pkg_free(_pv_print_buffer);
+	_pv_print_buffer_slots_active = 0;
+	_pv_print_buffer_size_active = 0;
 	_pv_print_buffer = NULL;
 }
 
@@ -1916,8 +1923,8 @@ void pv_destroy_buffer(void)
  */
 int pv_reinit_buffer(void)
 {
-	if(_pv_print_buffer_size==PV_DEFAULT_PRINT_BUFFER_SIZE
-			&& _pv_print_buffer_slots==PV_DEFAULT_PRINT_BUFFER_SLOTS)
+	if(_pv_print_buffer_size==_pv_print_buffer_size_active
+			&& _pv_print_buffer_slots==_pv_print_buffer_slots_active)
 		return 0;
 	pv_destroy_buffer();
 	return pv_init_buffer();