Ver código fonte

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 anos atrás
pai
commit
95805adb74
77 arquivos alterados com 2302 adições e 34 exclusões
  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)
 static int lua_sr_registrar_lookup(lua_State *L)
 {
 {
 	int ret;
 	int ret;
-	char *table;
+	char *table = NULL;
+	str uri = {NULL, 0};
 	sr_lua_env_t *env_L;
 	sr_lua_env_t *env_L;
 
 
 	env_L = sr_lua_env_get();
 	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");
 		LM_WARN("invalid parameters from Lua env\n");
 		return app_lua_return_error(L);
 		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");
 		LM_WARN("invalid number of parameters from Lua\n");
 		return app_lua_return_error(L);
 		return app_lua_return_error(L);
 	}
 	}
-	table  = (char*)lua_tostring(L, -1);
 	if(table==NULL || strlen(table)==0)
 	if(table==NULL || strlen(table)==0)
 	{
 	{
 		LM_WARN("invalid parameters from Lua\n");
 		LM_WARN("invalid parameters from Lua\n");
 		return app_lua_return_error(L);
 		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);
 	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.3. remove_body()
         1.2.4. keep_hf(regexp)
         1.2.4. keep_hf(regexp)
         1.2.5. fnmatch(value, expr [, flags])
         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
 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>
 		</example>
 	</section>
 	</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>
 </section>

+ 1394 - 0
modules/textopsx/textopsx.c

@@ -35,6 +35,10 @@
 #include "../../msg_translator.h"
 #include "../../msg_translator.h"
 #include "../../tcp_options.h"
 #include "../../tcp_options.h"
 #include "../../mod_fix.h"
 #include "../../mod_fix.h"
+#include "../../parser/parse_hname2.h"
+#include "../../select.h"
+#include "../../select_buf.h"
+
 
 
 #include "api.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 fixup_fnmatch(void** param, int param_no);
 
 
 static int w_remove_body_f(struct sip_msg*, char*, char *);
 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 bind_textopsx(textopsx_api_t *tob);
 
 
 static int mod_init(void);
 static int mod_init(void);
 
 
+extern select_row_t sel_declaration[];
+
 /* cfg functions */
 /* cfg functions */
 static cmd_export_t cmds[] = {
 static cmd_export_t cmds[] = {
 	{"msg_apply_changes",    (cmd_function)msg_apply_changes_f,     0,
 	{"msg_apply_changes",    (cmd_function)msg_apply_changes_f,     0,
@@ -70,6 +90,26 @@ static cmd_export_t cmds[] = {
 		fixup_fnmatch, ANY_ROUTE },
 		fixup_fnmatch, ANY_ROUTE },
 	{"fnmatch",              (cmd_function)w_fnmatch3_f,            3,
 	{"fnmatch",              (cmd_function)w_fnmatch3_f,            3,
 		fixup_fnmatch, ANY_ROUTE },
 		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,
 	{"bind_textopsx",        (cmd_function)bind_textopsx,           1,
 		0, ANY_ROUTE },
 		0, ANY_ROUTE },
 
 
@@ -99,6 +139,7 @@ static int mod_init(void)
 #ifdef USE_TCP
 #ifdef USE_TCP
 	tcp_set_clone_rcvbuf(1);
 	tcp_set_clone_rcvbuf(1);
 #endif
 #endif
+	register_select_table(sel_declaration);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -428,3 +469,1356 @@ static int bind_textopsx(textopsx_api_t *tob){
 	tob->msg_apply_changes = msg_apply_changes_f;
 	tob->msg_apply_changes = msg_apply_changes_f;
 	return 0;
 	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, 
 int	dlg_set_timeout_by_profile(struct dlg_profile_table *profile, 
 				   str *value, int timeout) 
 				   str *value, int timeout) 
 {
 {
-	unsigned int		i;
+	unsigned int		i = 0;
+	dlg_cell_t		*this_dlg = NULL;
 	struct dlg_profile_hash	*ph = 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 
 	/* If the profile has no value, iterate through every 
 	 * node and set its timeout.
 	 * node and set its timeout.
 	 */
 	 */
@@ -730,11 +744,23 @@ int	dlg_set_timeout_by_profile(struct dlg_profile_table *profile,
 			if(!ph) continue;
 			if(!ph) continue;
 			
 			
 			do { 
 			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;
 					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;
 				ph = ph->next;
 			} while(ph != profile->entries[i].first);
 			} 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;
 		ph = profile->entries[i].first;
 
 
 		if(ph) {
 		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;
 						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);
 		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;
 	return 0;
 }
 }
 
 

+ 1 - 1
modules_k/domain/api.h

@@ -32,7 +32,7 @@ typedef struct domain_api {
 } domain_api_t;
 } domain_api_t;
 
 
 typedef int (*bind_domain_t)(domain_api_t* api);
 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
 #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,
 	{"lookup_domain", (cmd_function)w_lookup_domain, 2, fixup_pvar_str,
 	 fixup_free_pvar_str,
 	 fixup_free_pvar_str,
 	 REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
 	 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, 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_t *ht;
 	ht_cell_t *htc;	/*!< One HT cell */
 	ht_cell_t *htc;	/*!< One HT cell */
 	void* th;
 	void* th;
-	void* ih;
 	void* vh;
 	void* vh;
 
 
 	if (rpc->scan(c, "SS", &htname, &keyname) < 2) {
 	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
               7.1. nh_enable_ping
 
 
+        8. Selects
+
+              8.1. @nathelper.rewrite_contact[n]
+
    2. Frequently Asked Questions
    2. Frequently Asked Questions
 
 
    List of Examples
    List of Examples
@@ -102,6 +106,7 @@ Ovidiu Sas
    1.18. $rr_count usage
    1.18. $rr_count usage
    1.19. $rr_top_count usage
    1.19. $rr_top_count usage
    1.20. nh_enable_ping usage
    1.20. nh_enable_ping usage
+   1.21. @nathelper.rewrite_contact usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -148,6 +153,10 @@ Chapter 1. Admin Guide
 
 
         7.1. nh_enable_ping
         7.1. nh_enable_ping
 
 
+   8. Selects
+
+        8.1. @nathelper.rewrite_contact[n]
+
 1. Overview
 1. Overview
 
 
    This is a module to help with NAT traversal and reuse of tcp
    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.7. add_contact_alias([ip_addr, port, proto])
    5.8. handle_ruri_alias()
    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.
    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();};
 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
    Alters the SDP information in orer to facilitate NAT traversal. What
    changes to be performed may be controled via the "flags" parameter.
    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");};
 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
    Add received parameter to Contact header fields or Contact URI. The
    parameter will contain URI created from the source IP, port, and
    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
 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
    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
    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();
 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
    Tries to guess if client's request originated behind a nat. The
    parameter determines what heuristics is used.
    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
      * 32 - test if the source IP address of signaling is a RFC1918
        address
        address
      * 64 - test if the source connection of signaling is a WebSocket
      * 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
    All flags can be bitwise combined, the test returns true if any of the
    tests identified a NAT.
    tests identified a NAT.
@@ -501,14 +513,14 @@ fix_nated_register();
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
    FAILURE_ROUTE, BRANCH_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
    Determines if the address in the parameter is an rfc1918 address. The
    parameter allows pseudo-variables usage.
    parameter allows pseudo-variables usage.
 
 
    This function can be used from ANY_ROUTE.
    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
    Adds ;alias=ip~port~transport parameter to contact URI containing
    either received ip, port, and transport protocol or those given as
    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
    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
    $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
 $ 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
 Chapter 2. Frequently Asked Questions
 
 
    2.1. What happend with "rtpproxy_disable" parameter?
    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
 			<emphasis>64</emphasis> -  test if the source connection of
 			signaling is a WebSocket
 			signaling is a WebSocket
 			</para></listitem>
 			</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>
 			</itemizedlist>
 		<para>
 		<para>
 		All flags can be bitwise combined, the test returns true if any of 
 		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>
 
 
+	<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>
 </chapter>
 
 

+ 110 - 1
modules_k/nathelper/nathelper.c

@@ -220,6 +220,7 @@
 #include "../../socket_info.h"
 #include "../../socket_info.h"
 #include "../../mod_fix.h"
 #include "../../mod_fix.h"
 #include "../../dset.h"
 #include "../../dset.h"
+#include "../../select.h"
 #include "../registrar/sip_msg.h"
 #include "../registrar/sip_msg.h"
 #include "../usrloc/usrloc.h"
 #include "../usrloc/usrloc.h"
 #include "nathelper.h"
 #include "nathelper.h"
@@ -243,6 +244,7 @@ MODULE_VERSION
 #define	NAT_UAC_TEST_RPORT	0x10
 #define	NAT_UAC_TEST_RPORT	0x10
 #define	NAT_UAC_TEST_O_1918	0x20
 #define	NAT_UAC_TEST_O_1918	0x20
 #define NAT_UAC_TEST_WS		0x40
 #define NAT_UAC_TEST_WS		0x40
+#define	NAT_UAC_TEST_C_PORT	0x80
 
 
 
 
 #define DEFAULT_RTPP_SET_ID		0
 #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
 static int
 fixup_fix_sdp(void** param, int param_no)
 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;
 		nets_1918[i].netaddr = ntohl(addr.s_addr) & nets_1918[i].mask;
 	}
 	}
 
 
+	register_select_table(sel_declaration);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1372,6 +1397,27 @@ contact_1918(struct sip_msg* msg)
 	return (is1918addr(&(uri.host)) == 1) ? 1 : 0;
 	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
  * 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;
 		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)
 	if ((tests & NAT_UAC_TEST_WS)
 		&& (msg->rcv.proto == PROTO_WS || msg->rcv.proto == PROTO_WSS))
 		&& (msg->rcv.proto == PROTO_WS || msg->rcv.proto == PROTO_WSS))
 		return 1;
 		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 */
 	/* no test succeeded */
 	return -1;
 	return -1;
 
 
@@ -2292,3 +2345,59 @@ done:
 	/* let the core handle further the reply */
 	/* let the core handle further the reply */
 	return 1;
 	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);
 	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       = regapi_save;
 	api->save_uri   = regapi_save_uri;
 	api->save_uri   = regapi_save_uri;
 	api->lookup     = regapi_lookup;
 	api->lookup     = regapi_lookup;
+	api->lookup_uri = regapi_lookup_uri;
 	api->registered = regapi_registered;
 	api->registered = regapi_registered;
 
 
 	return 0;
 	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);
 typedef int (*regapi_lookup_f)(struct sip_msg *msg, char *table);
 int regapi_lookup(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
  * @brief REGISTRAR API structure
  */
  */
@@ -47,6 +50,7 @@ typedef struct registrar_api {
 	regapi_save_f       save;
 	regapi_save_f       save;
 	regapi_save_uri_f   save_uri;
 	regapi_save_uri_f   save_uri;
 	regapi_lookup_f     lookup;
 	regapi_lookup_f     lookup;
+	regapi_lookup_uri_f lookup_uri;
 	regapi_lookup_f     registered;
 	regapi_lookup_f     registered;
 } registrar_api_t;
 } 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 \
 	kxml kperl ksnmpstats kxmpp kcarrierroute kberkeley kldap kutils \
 	kpurple ktls kwebsocket kpresence klua kpython kgeoip ksqlite kjson \
 	kpurple ktls kwebsocket kpresence klua kpython kgeoip ksqlite kjson \
 	kredis kmono" \
 	kredis kmono" \
-	include_modules="cdp mangler xhttp_pi"
+	include_modules="cdp mangler print_lib xhttp_pi"
 %else
 %else
 make every-module skip_modules="auth_identity db_cassandra iptrtpproxy\
 make every-module skip_modules="auth_identity db_cassandra iptrtpproxy\
 	db_oracle memcached mi_xmlrpc osp" \
 	db_oracle memcached mi_xmlrpc osp" \
 	group_include="kstandard kmysql kpostgres kcpl kunixodbc \
 	group_include="kstandard kmysql kpostgres kcpl kunixodbc \
 	kxml kperl ksnmpstats kxmpp kberkeley kldap kutils \
 	kxml kperl ksnmpstats kxmpp kberkeley kldap kutils \
 	kpurple ktls kwebsocket kpresence klua kpython ksqlite" \
 	kpurple ktls kwebsocket kpresence klua kpython ksqlite" \
-	include_modules="cdp mangler xhttp_pi"
+	include_modules="cdp mangler print_lib xhttp_pi"
 %endif
 %endif
 make utils
 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\
 	kxml kperl ksnmpstats kxmpp kcarrierroute kberkeley kldap kutils\
 	kpurple ktls kwebsocket kpresence klua kpython kgeoip ksqlite kjson\
 	kpurple ktls kwebsocket kpresence klua kpython kgeoip ksqlite kjson\
 	kredis kmono" \
 	kredis kmono" \
-	include_modules="cdp mangler xhttp_pi"
+	include_modules="cdp mangler print_lib xhttp_pi"
 
 
 mkdir -p $RPM_BUILD_ROOT/%{_unitdir}
 mkdir -p $RPM_BUILD_ROOT/%{_unitdir}
 install -m644 pkg/kamailio/fedora/%{?fedora}/kamailio.service \
 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 \
 	group_include="kstandard kmysql kpostgres kcpl kunixodbc \
 	kxml kperl ksnmpstats kxmpp kberkeley kldap kutils \
 	kxml kperl ksnmpstats kxmpp kberkeley kldap kutils \
 	kpurple ktls kwebsocket kpresence klua kpython ksqlite" \
 	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
 mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d
 install -m755 pkg/kamailio/centos/%{?centos}/kamailio.init \
 install -m755 pkg/kamailio/centos/%{?centos}/kamailio.init \
@@ -496,6 +496,8 @@ fi
 %doc %{_docdir}/kamailio/modules/README.pdb
 %doc %{_docdir}/kamailio/modules/README.pdb
 %doc %{_docdir}/kamailio/modules/README.pipelimit
 %doc %{_docdir}/kamailio/modules/README.pipelimit
 %doc %{_docdir}/kamailio/modules/README.prefix_route
 %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.ratelimit
 %doc %{_docdir}/kamailio/modules/README.rtpproxy
 %doc %{_docdir}/kamailio/modules/README.rtpproxy
 %doc %{_docdir}/kamailio/modules/README.sanity
 %doc %{_docdir}/kamailio/modules/README.sanity
@@ -510,6 +512,11 @@ fi
 %doc %{_docdir}/kamailio/modules/README.tm
 %doc %{_docdir}/kamailio/modules/README.tm
 %doc %{_docdir}/kamailio/modules/README.tmrec
 %doc %{_docdir}/kamailio/modules/README.tmrec
 %doc %{_docdir}/kamailio/modules/README.topoh
 %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
 %doc %{_docdir}/kamailio/modules/README.xhttp_rpc
 %doc %{_docdir}/kamailio/modules/README.xhttp_rpc
 %doc %{_docdir}/kamailio/modules/README.xlog
 %doc %{_docdir}/kamailio/modules/README.xlog
@@ -587,6 +594,9 @@ fi
 %{_libdir}/kamailio/libkmi.so
 %{_libdir}/kamailio/libkmi.so
 %{_libdir}/kamailio/libkmi.so.1
 %{_libdir}/kamailio/libkmi.so.1
 %{_libdir}/kamailio/libkmi.so.1.0
 %{_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
 %{_libdir}/kamailio/libsrdb1.so.1
 %{_libdir}/kamailio/libsrdb1.so.1
 %{_libdir}/kamailio/libsrdb1.so.1.0
 %{_libdir}/kamailio/libsrdb1.so.1.0
@@ -627,6 +637,8 @@ fi
 %{_libdir}/kamailio/modules/pdb.so
 %{_libdir}/kamailio/modules/pdb.so
 %{_libdir}/kamailio/modules/pipelimit.so
 %{_libdir}/kamailio/modules/pipelimit.so
 %{_libdir}/kamailio/modules/prefix_route.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/ratelimit.so
 %{_libdir}/kamailio/modules/rtpproxy.so
 %{_libdir}/kamailio/modules/rtpproxy.so
 %{_libdir}/kamailio/modules/sanity.so
 %{_libdir}/kamailio/modules/sanity.so
@@ -641,6 +653,11 @@ fi
 %{_libdir}/kamailio/modules/tm.so
 %{_libdir}/kamailio/modules/tm.so
 %{_libdir}/kamailio/modules/tmrec.so
 %{_libdir}/kamailio/modules/tmrec.so
 %{_libdir}/kamailio/modules/topoh.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.so
 %{_libdir}/kamailio/modules/xhttp_rpc.so
 %{_libdir}/kamailio/modules/xhttp_rpc.so
 %{_libdir}/kamailio/modules/xlog.so
 %{_libdir}/kamailio/modules/xlog.so
@@ -1006,6 +1023,8 @@ fi
 %changelog
 %changelog
 * Fri Dec 21 2012 Peter Dunkley <[email protected]>
 * Fri Dec 21 2012 Peter Dunkley <[email protected]>
   - Added db2_ldap, db2_ops, and timer to the build
   - 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]>
 * Thu Dec 13 2012 Peter Dunkley <[email protected]>
   - Added xhttp_pi framework examples to the installation
   - Added xhttp_pi framework examples to the installation
   - Added xhttp_pi README 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;
 static char **_pv_print_buffer = NULL;
 #define PV_DEFAULT_PRINT_BUFFER_SIZE 1024
 #define PV_DEFAULT_PRINT_BUFFER_SIZE 1024
 static int _pv_print_buffer_size  = PV_DEFAULT_PRINT_BUFFER_SIZE;
 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 */
 /* 6 mod params + 4 direct usage from mods */
 #define PV_DEFAULT_PRINT_BUFFER_SLOTS 10
 #define PV_DEFAULT_PRINT_BUFFER_SLOTS 10
 static int _pv_print_buffer_slots = PV_DEFAULT_PRINT_BUFFER_SLOTS;
 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;
 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",
 	LM_DBG("PV print buffer initialized to [%d][%d]\n",
 			_pv_print_buffer_slots, _pv_print_buffer_size);
 			_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;
 	return 0;
 }
 }
 
 
@@ -1902,12 +1907,14 @@ void pv_destroy_buffer(void)
 
 
 	if(_pv_print_buffer==NULL)
 	if(_pv_print_buffer==NULL)
 		return;
 		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)
 		if(_pv_print_buffer[i]!=NULL)
 			pkg_free(_pv_print_buffer[i]);
 			pkg_free(_pv_print_buffer[i]);
 	}
 	}
 	pkg_free(_pv_print_buffer);
 	pkg_free(_pv_print_buffer);
+	_pv_print_buffer_slots_active = 0;
+	_pv_print_buffer_size_active = 0;
 	_pv_print_buffer = NULL;
 	_pv_print_buffer = NULL;
 }
 }
 
 
@@ -1916,8 +1923,8 @@ void pv_destroy_buffer(void)
  */
  */
 int pv_reinit_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;
 		return 0;
 	pv_destroy_buffer();
 	pv_destroy_buffer();
 	return pv_init_buffer();
 	return pv_init_buffer();