Просмотр исходного кода

Arrghh. Poor memory. My apologies for this commit.

Merge branch 'master' of ssh://git.sip-router.org/sip-router
Olle E. Johansson 11 лет назад
Родитель
Сommit
6bc322ee1a
100 измененных файлов с 3055 добавлено и 939 удалено
  1. 2 0
      INSTALL
  2. 1 0
      README-MODULES
  3. 2 1
      cfg.y
  4. 19 0
      forward.c
  5. 4 0
      forward.h
  6. 12 0
      main.c
  7. 9 0
      modules/app_java/Makefile
  8. 80 74
      modules/app_java/README
  9. 5 5
      modules/app_java/doc/app_java.xml
  10. 74 62
      modules/app_java/doc/app_java_admin.xml
  11. 2 0
      modules/app_java/global.h
  12. 7 7
      modules/app_java/java_iface.c
  13. 4 4
      modules/app_java/java_mod.c
  14. 16 16
      modules/app_java/java_msgobj.c
  15. 28 28
      modules/app_java/java_native_methods.c
  16. 26 26
      modules/app_java/java_sig_parser.c
  17. 8 8
      modules/app_java/java_support.c
  18. 2 2
      modules/app_java/utils.c
  19. 24 8
      modules/app_perl/README
  20. 9 0
      modules/app_perl/app_perl_mod.c
  21. 19 0
      modules/app_perl/doc/app_perl_admin.xml
  22. 19 9
      modules/auth_db/authorize.c
  23. 141 0
      modules/cdp/cdp_rpc.c
  24. 16 0
      modules/cdp/cdp_rpc.h
  25. 1 1
      modules/cdp/config.h
  26. 2 1
      modules/cdp/diameter_comm.c
  27. 21 3
      modules/cdp/doc/cdp_admin.xml
  28. 7 1
      modules/cdp/mod.c
  29. 2 0
      modules/cdp/peer.h
  30. 16 2
      modules/cdp/peermanager.c
  31. 42 4
      modules/cdp/routing.c
  32. 5 0
      modules/cnxcc/cnxcc_mod.c
  33. 1 0
      modules/db_flatstore/km_flat_con.c
  34. 2 2
      modules/db_mysql/km_dbase.c
  35. 4 3
      modules/dialog/dlg_handlers.c
  36. 1 1
      modules/dispatcher/dispatch.c
  37. 50 13
      modules/dispatcher/dispatcher.c
  38. 1 0
      modules/ims_auth/api.h
  39. 1 0
      modules/ims_auth/authims_mod.c
  40. 187 27
      modules/ims_auth/authorize.c
  41. 1 0
      modules/ims_auth/authorize.h
  42. 21 18
      modules/ims_auth/cxdx_mar.c
  43. 1 0
      modules/ims_auth/cxdx_mar.h
  44. 3 2
      modules/ims_charging/ccr.c
  45. 0 1
      modules/ims_charging/config.h
  46. 24 49
      modules/ims_charging/ims_ro.c
  47. 1 4
      modules/ims_charging/mod.c
  48. 53 0
      modules/ims_charging/ro_avp.c
  49. 17 0
      modules/ims_charging/ro_avp.h
  50. 4 0
      modules/ims_registrar_pcscf/notify.c
  51. 2 1
      modules/ims_registrar_scscf/registrar_notify.c
  52. 3 4
      modules/ims_usrloc_pcscf/udomain.c
  53. 6 0
      modules/ims_usrloc_scscf/usrloc_db.c
  54. 1 0
      modules/ipops/ipops_pv.c
  55. 3 1
      modules/kex/core_stats.c
  56. 4 4
      modules/mtree/mtree_mod.c
  57. 4 2
      modules/presence_dialoginfo/notify_body.c
  58. 5 0
      modules/pv/pv_core.c
  59. 5 5
      modules/pv/pv_trans.c
  60. 8 2
      modules/rls/list.h
  61. 1 1
      modules/rls/notify.c
  62. 2 0
      modules/rls/subscribe.c
  63. 1 1
      modules/rls/utils.h
  64. 22 19
      modules/sdpops/README
  65. 4 0
      modules/sdpops/doc/sdpops_admin.xml
  66. 1 1
      modules/siputils/checks.c
  67. 10 5
      modules/siputils/sipops.c
  68. 268 301
      modules/textops/README
  69. 140 0
      modules/textops/doc/textops_admin.xml
  70. 673 94
      modules/textops/textops.c
  71. 1 0
      modules/tls/tls_mod.c
  72. 10 0
      modules/tls/tls_select.c
  73. 3 0
      modules/tls/tls_select.h
  74. 46 0
      modules/tls/tls_server.c
  75. 2 0
      modules/tls/tls_server.h
  76. 1 1
      modules/topoh/th_msg.c
  77. 30 11
      modules/utils/README
  78. 1 1
      modules/utils/doc/utils_admin.xml
  79. 15 7
      modules/utils/functions.c
  80. 161 19
      modules/websocket/ws_conn.c
  81. 8 0
      modules/websocket/ws_conn.h
  82. 122 58
      modules/websocket/ws_frame.c
  83. 3 1
      modules/websocket/ws_handshake.c
  84. 299 3
      msg_translator.c
  85. 3 0
      msg_translator.h
  86. 7 5
      onsend.h
  87. 2 1
      parser/sdp/sdp.c
  88. 1 0
      pkg/kamailio/deb/debian/control
  89. 1 0
      pkg/kamailio/deb/jessie/control
  90. 1 0
      pkg/kamailio/deb/precise/control
  91. 1 0
      pkg/kamailio/deb/squeeze/control
  92. 1 0
      pkg/kamailio/deb/wheezy/control
  93. 1 1
      pkg/kamailio/rpm/kamailio.init
  94. 4 8
      route.c
  95. 1 0
      rvalue.c
  96. 94 0
      sr_module.c
  97. 11 0
      sr_module.h
  98. 45 0
      str.c
  99. 9 0
      str.h
  100. 11 0
      tcp_main.c

+ 2 - 0
INSTALL

@@ -131,6 +131,8 @@ Requirements:
 - libsctp devel headers - if you want to compile the SCTP transport in the core
 - libssl devel headers (openssl project) - if you want to compule the TLS module
 - linunistring - for the Websockets module
+- python and devel headers for python module
+- jdk and gcj for java module
 
 
 OS Notes:

+ 1 - 0
README-MODULES

@@ -37,6 +37,7 @@ app_lua			Lang :: Execute embedded LUA scripts
 app_mono		Lang :: Execute embedded MONO scripts (like C#, Java, javascript)
 app_perl		Lang :: Embedded perl script support
 app_python		Lang :: Execute embedded Python scripts
+app_java		Lang :: Execute embedded Java compiled code
 async			Asynchronus SIP request handling functions
 auth			MD5 digest authentication support
 auth_db			Authentication using a database module

+ 2 - 1
cfg.y

@@ -3275,7 +3275,8 @@ cmd:
 					LOG(L_ERR, "misused command %s\n", $1);
 					yyerror("Command cannot be used in the block\n");
 			} else {
-				LOG(L_ERR, "cfg. parser: failed to find command %s\n", $1);
+				LOG(L_ERR, "cfg. parser: failed to find command %s (params %ld)\n",
+						$1, mod_func_action->val[1].u.number);
 				yyerror("unknown command, missing loadmodule?\n");
 			}
 			free_mod_func_action(mod_func_action);

+ 19 - 0
forward.c

@@ -121,6 +121,12 @@
 static int mhomed_sock_cache_disabled = 0;
 static int sock_inet = -1;
 static int sock_inet6 = -1;
+static int _forward_set_send_info = 0;
+
+void forward_set_send_info(int v)
+{
+	_forward_set_send_info = v;
+}
 
 static void apply_force_send_socket(struct dest_info* dst, struct sip_msg* msg);
 
@@ -497,6 +503,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 	int ret;
 	struct ip_addr ip; /* debugging only */
 	char proto;
+	struct onsend_info onsnd_info = {0};
 #ifdef USE_DNS_FAILOVER
 	struct socket_info* prev_send_sock;
 	int err;
@@ -623,7 +630,18 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 			}
 		}
 #endif
+
+		if(unlikely(_forward_set_send_info==1)) {
+			onsnd_info.to=&send_info->to;
+			onsnd_info.send_sock=send_info->send_sock;
+			onsnd_info.buf=buf;
+			onsnd_info.len=len;
+			onsnd_info.msg=msg;
+			p_onsend=&onsnd_info;
+		}
+
 		if (msg_send(send_info, buf, len)<0){
+			p_onsend=0;
 			ret=ser_error=E_SEND;
 #ifdef USE_DST_BLACKLIST
 			(void)dst_blacklist_add(BLST_ERR_SEND, send_info, msg);
@@ -634,6 +652,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 			goto error;
 #endif
 		}else{
+			p_onsend=0;
 			ret=ser_error=E_OK;
 			/* sent requests stats */
 			STATS_TX_REQUEST(  msg->first_line.u.request.method_value );

+ 4 - 0
forward.h

@@ -84,6 +84,8 @@ inline static struct socket_info* get_send_socket(struct sip_msg* msg,
 }
 
 
+#define GET_URI_PORT(uri) ((uri)->port_no?(uri)->port_no:(((uri)->proto==PROTO_TLS)?SIPS_PORT:SIP_PORT))
+
 struct socket_info* get_out_socket(union sockaddr_union* to, int proto);
 typedef int (*check_self_f)(str* host, unsigned short port,
 		unsigned short proto);
@@ -107,6 +109,8 @@ int update_sock_struct_from_via( union sockaddr_union* to,
 int forward_reply( struct sip_msg* msg);
 int forward_reply_nocb( struct sip_msg* msg);
 
+void forward_set_send_info(int v);
+
 int is_check_self_func_list_set(void);
 
 

+ 12 - 0
main.c

@@ -1787,6 +1787,18 @@ static int calc_proc_no(void)
 			 tcp_e_listeners = tcp_cfg_children_no;
 	}
 	tcp_listeners += tcp_e_listeners;
+#ifdef USE_TLS
+	tcp_e_listeners = 0;
+	for (si=tls_listen, tcp_e_listeners=0; si; si=si->next) {
+		if(si->workers>0)
+			tcp_listeners += si->workers;
+		else {
+			if(tcp_listeners==0)
+				tcp_e_listeners = tcp_cfg_children_no;
+		}
+	}
+	tcp_listeners += tcp_e_listeners;
+#endif
 	tcp_children_no = tcp_listeners;
 #endif
 #ifdef USE_SCTP

+ 9 - 0
modules/app_java/Makefile

@@ -20,6 +20,15 @@ JAVA_HOME ?= $(shell readlink -f /usr/bin/javac | sed "s:bin/javac::")
 DEFS += $(shell pkg-config libgcj12 --cflags) -I$(JAVA_HOME)/include
 LIBS += $(shell pkg-config libgcj12 --libs) -L$(JAVA_HOME)/lib  -ljvm
 
+# On Debian 7.5 there is a bug with JAVA_HOME detection.
+# $(shell readlink -f /usr/bin/javac | sed "s:bin/javac::") points to perl wrapper script (/usr/bin/gcj-wrapper-4.7)
+# whereas the real compiler is at /usr/bin/gcj-4.7. As the result, JAVA_HOME will not be a directory, that is incorrect.
+# At this point I don't see any universal method as explicit setting this variable at the compile phase.
+# -- ez
+ifeq ($(shell [ -d "${JAVA_HOME}" -a -f "$(JAVA_HOME)/include/jni.h" -a -f "$(JAVA_HOME)/lib/libjvm.so" ] && echo 1 || echo 0),0)
+    $(error Can't locate Java Development Kit. You have to specify environment JAVA_HOME to build app_java)
+endif
+
 ifeq ($(OS), freebsd)
 LIBS+=-pthread
 endif

+ 80 - 74
modules/app_java/README

@@ -6,7 +6,7 @@ Edited by
 
 Konstantin Mosesov
 
-   Copyright © 2013 Konstantin Mosesov
+   Copyright © 2013, 2014 Konstantin Mosesov
      __________________________________________________________________
 
    Table of Contents
@@ -21,7 +21,7 @@ Konstantin Mosesov
 
         3. Java runtime
 
-              3.1.
+              3.1. JRE or JDK is required to use this module
 
         4. Parameters
 
@@ -84,7 +84,7 @@ Chapter 1. Admin Guide
 
    3. Java runtime
 
-        3.1.
+        3.1. JRE or JDK is required to use this module
 
    4. Parameters
 
@@ -155,11 +155,12 @@ Chapter 1. Admin Guide
 
 3. Java runtime
 
-   3.1.
+   3.1. JRE or JDK is required to use this module
 
-3.1.
+3.1. JRE or JDK is required to use this module
 
-   Java runtime library (JRE or JDK) is required to use this module.
+   Java runtime library (JRE and JDK for building app_java) is required to
+   use this module.
 
 4. Parameters
 
@@ -205,24 +206,25 @@ modparam("app_java", "java_options", "-Djava.compiler=NONE")
    Example 1.4. Set java_options parameter (live configuration)
 ...
 # Assumes "application java folder" is located at /opt/kamailio/java
-modparam("app_java", "java_options", "-Djava.compiler=NONE -Djava.class.path=/pa
-th/to/kamailio/modules:/opt/kamailio/java:/opt/kamailio/java/kamailio.jar")
+modparam("app_java", "java_options", "-Djava.compiler=NONE
+    -Djava.class.path=/path/to/kamailio/modules:/opt/kamailio/java:
+    /opt/kamailio/java/kamailio.jar")
 ...
 
    Example 1.5. Set java_options parameter (verbose configuration)
 ...
 # Assumes "application java folder" is located at /opt/kamailio/java
-modparam("app_java", "java_options", "-verbose:gc,class,jni -Djava.compiler=NONE
- -Djava.class.path=/path/to/kamailio/modules:/opt/kamailio/java:/opt/kamailio/ja
-va/kamailio.jar")
+modparam("app_java", "java_options", "-verbose:gc,class,jni
+    -Djava.compiler=NONE -Djava.class.path=/path/to/kamailio/modules:
+    /opt/kamailio/java:/opt/kamailio/java/kamailio.jar")
 ...
 
    Example 1.6. Set java_options parameter (debug configuration)
 ...
 # Assumes "application java folder" is located at /opt/kamailio/java
-modparam("app_java", "java_options", "-Xdebug -verbose:gc,class,jni -Djava.compi
-ler=NONE -Djava.class.path=/path/to/kamailio/modules:/opt/kamailio/java:/opt/kam
-ailio/java/kamailio.jar")
+modparam("app_java", "java_options", "-Xdebug -verbose:gc,class,jni
+    -Djava.compiler=NONE -Djava.class.path=/path/to/kamailio/modules:
+    /opt/kamailio/java:/opt/kamailio/java/kamailio.jar")
 ...
 
 4.4. force_cmd_exec (int)
@@ -269,8 +271,8 @@ modparam("app_java", "force_cmd_exec", 1)
                 object       L
                 short        S
                 void         V
-                Note that to specify an object, the "L" is followed by the objec
-t's class name and ends with a semi-colon, ';' .
+                Note that to specify an object, the "L" is followed by the
+                object's class name and ends with a semi-colon, ';' .
 
    app_java supports the following signatures:
                 Primitives: Z,B,C,D,F,I,J,L,S,V
@@ -294,7 +296,8 @@ ure.
         Parameters count should be exactly the same as signature count.
         Note 1: Arrays representation (symbol '[') is not supported yet.
         Note 2: You shall use a correct signature, e.g. the following examples o
-f combinations are invalid:
+f
+        combinations are invalid:
         java_method_exec("ExampleMethod", "ZI", "False");
         java_method_exec("ExampleMethod", "LI", "something", "5");
 
@@ -314,8 +317,8 @@ java_method_exec("ExampleMethod", "V");
 # Java
 public int ExampleMethod()
 {
-                ... do something;
-                return 1;
+    ... do something;
+    return 1;
 }
      * Example 1.9. Signature: "Ljava/lang/String;I"
        Kamailio prototype
@@ -331,8 +334,8 @@ java_method_exec("ExampleMethod", "Ljava/lang/String;I", "$mb", "$ml");
 # Java
 public int ExampleMethod(String SipMessageBuffer, int SipMessageLenght)
 {
-                ... do something with buffer;
-                return 1;
+    ... do something with buffer;
+    return 1;
 }
      * Example 1.10. Signature: "ZB"
        Kamailio prototype
@@ -348,11 +351,12 @@ java_method_exec("ExampleMethod", "ZB", "true", "0x05");
 # Java
 public int ExampleMethod(boolean flagSet, byte bFlag);
 {
-                if (flagSet)
-                {
-                        ... do something with flags;
-                }
-                return 1;
+    if (flagSet)
+    {
+        ... do something with flags;
+    }
+
+    return 1;
 }
 
 5.3. java_staticmethod_exec(method, method_signature, [param1[, param2[,
@@ -372,8 +376,8 @@ java_staticmethod_exec("ExampleMethod", "V");
 # Java
 public static int ExampleMethod()
 {
-                ... do something;
-                return 1;
+    ... do something;
+    return 1;
 }
      * Example 1.12. Signature: "Ljava/lang/String;I"
        Kamailio prototype
@@ -390,8 +394,8 @@ java_staticmethod_exec("ExampleMethod", "Ljava/lang/String;I", "$mb", "$ml");
 # Java
 public static int ExampleMethod(String SipMessageBuffer, int SipMessageLenght)
 {
-                ... do something with buffer;
-                return 1;
+    ... do something with buffer;
+    return 1;
 }
      * Example 1.13. Signature: "ZB"
        Kamailio prototype
@@ -407,11 +411,12 @@ java_staticmethod_exec("ExampleMethod", "ZB", "true", "0x05");
 # Java
 public static int ExampleMethod(boolean flagSet, byte bFlag);
 {
-                if (flagSet)
-                {
-                        ... do something with flags;
-                }
-                return 1;
+    if (flagSet)
+    {
+        ... do something with flags;
+    }
+
+    return 1;
 }
 
 5.4. java_s_method_exec(method, method_signature, [param1[, param2[, ...]]])
@@ -432,8 +437,8 @@ java_s_method_exec("ExampleMethod", "V");
 # Java
 public synchronized int ExampleMethod()
 {
-                ... do something;
-                return 1;
+    ... do something;
+    return 1;
 }
      * Example 1.15. Signature: "Ljava/lang/String;I"
        Kamailio prototype
@@ -450,8 +455,8 @@ java_s_method_exec("ExampleMethod", "Ljava/lang/String;I", "$mb", "$ml");
 public synchronized int ExampleMethod(String SipMessageBuffer, int SipMessageLen
 ght)
 {
-                ... do something with buffer;
-                return 1;
+    ... do something with buffer;
+    return 1;
 }
      * Example 1.16. Signature: "ZB"
        Kamailio prototype
@@ -467,11 +472,12 @@ java_s_method_exec("ExampleMethod", "ZB", "true", "0x05");
 # Java
 public synchronized int ExampleMethod(boolean flagSet, byte bFlag);
 {
-                if (flagSet)
-                {
-                        ... do something with flags;
-                }
-                return 1;
+    if (flagSet)
+    {
+        ... do something with flags;
+    }
+
+    return 1;
 }
 
 5.5. java_s_staticmethod_exec(method, method_signature, [param1[, param2[,
@@ -493,8 +499,8 @@ java_s_staticmethod_exec("ExampleMethod", "V");
 # Java
 public static synchronized int ExampleMethod()
 {
-                ... do something;
-                return 1;
+    ... do something;
+    return 1;
 }
      * Example 1.18. Signature: "Ljava/lang/String;I"
        Kamailio prototype
@@ -512,8 +518,8 @@ java_s_staticmethod_exec("ExampleMethod", "Ljava/lang/String;I", "$mb", "$ml");
 public static synchronized int ExampleMethod(String SipMessageBuffer, int SipMes
 sageLenght)
 {
-                ... do something with buffer;
-                return 1;
+    ... do something with buffer;
+    return 1;
 }
      * Example 1.19. Signature: "ZB"
        Kamailio prototype
@@ -529,11 +535,12 @@ java_s_staticmethod_exec("ExampleMethod", "ZB", "true", "0x05");
 # Java
 public static synchronized int ExampleMethod(boolean flagSet, byte bFlag);
 {
-                if (flagSet)
-                {
-                        ... do something with flags;
-                }
-                return 1;
+    if (flagSet)
+    {
+        ... do something with flags;
+    }
+
+    return 1;
 }
 
 6. Java Module API
@@ -549,25 +556,24 @@ import org.siprouter.NativeInterface.*;
 
 public class Kamailio extends NativeMethods
 {
-                /* Here you should specify a full path to app_java.so */
-                static
-                {
-                                System.load("/opt/kamailio/lib/kamailio/modules/
-app_java.so");
-                }
-
-                /* Constructor. Do not remove !!! */
-                public Kamailio()
-                {
-                }
-
-                /*
-                This method should be executed for each children process, immedi
-ately after forking.
-                Required. Do not remove !!!
-                */
-                public int child_init(int rank)
-                {
-                                return 1;
-                }
+    /* Here you should specify a full path to app_java.so */
+    static
+    {
+        System.load("/opt/kamailio/lib/kamailio/modules/app_java.so");
+    }
+
+    /* Constructor. Do not remove !!! */
+    public Kamailio()
+    {
+    }
+
+    /*
+        This method should be executed for each children process, immediately af
+ter forking.
+        Required. Do not remove !!!
+    */
+    public int child_init(int rank)
+    {
+        return 1;
+    }
 }

+ 5 - 5
modules/app_java/doc/app_java.xml

@@ -6,7 +6,6 @@
 <!-- Include general documentation entities -->
 <!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
 %docentities;
-
 ]>
 
 <book xmlns:xi="http://www.w3.org/2001/XInclude">
@@ -15,8 +14,8 @@
 	<productname class="trade">&kamailioname;</productname>
 	<authorgroup>
 	    <author>
-			<firstname>Konstantin</firstname>
-			<surname>Mosesov</surname>
+		<firstname>Konstantin</firstname>
+		<surname>Mosesov</surname>
 	    </author>
 	    <editor>
 	    	<firstname>Konstantin</firstname>
@@ -25,11 +24,12 @@
 	</authorgroup>
 	<copyright>
 	    <year>2013</year>
-		<holder>Konstantin Mosesov</holder>
+	    <year>2014</year>
+	    <holder>Konstantin Mosesov</holder>
 	</copyright>
     </bookinfo>
     <toc></toc>
     
-	<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="app_java_admin.xml"/>
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="app_java_admin.xml"/>
     
 </book>

+ 74 - 62
modules/app_java/doc/app_java_admin.xml

@@ -88,8 +88,8 @@
 	<section>
 		<title>Java runtime</title>
 		<section>
-			<title/>
-			<para>Java runtime library (JRE or JDK) is required to use this module.</para>
+			<title>JRE or JDK is required to use this module</title>
+			<para>Java runtime library (JRE and JDK for building app_java) is required to use this module.</para>
 		</section>
 	</section>
 	<!-- end of section Java Runtime -->
@@ -166,7 +166,9 @@ modparam("app_java", "java_options", "-Djava.compiler=NONE")
 				<programlisting format="linespecific">
 ...
 # Assumes "application java folder" is located at /opt/kamailio/java
-modparam("app_java", "java_options", "-Djava.compiler=NONE -Djava.class.path=/path/to/kamailio/modules:/opt/kamailio/java:/opt/kamailio/java/kamailio.jar")
+modparam("app_java", "java_options", "-Djava.compiler=NONE 
+    -Djava.class.path=/path/to/kamailio/modules:/opt/kamailio/java:
+    /opt/kamailio/java/kamailio.jar")
 ...
 </programlisting>
 			</example>
@@ -175,7 +177,9 @@ modparam("app_java", "java_options", "-Djava.compiler=NONE -Djava.class.path=/pa
 				<programlisting format="linespecific">
 ...
 # Assumes "application java folder" is located at /opt/kamailio/java
-modparam("app_java", "java_options", "-verbose:gc,class,jni -Djava.compiler=NONE -Djava.class.path=/path/to/kamailio/modules:/opt/kamailio/java:/opt/kamailio/java/kamailio.jar")
+modparam("app_java", "java_options", "-verbose:gc,class,jni 
+    -Djava.compiler=NONE -Djava.class.path=/path/to/kamailio/modules:
+    /opt/kamailio/java:/opt/kamailio/java/kamailio.jar")
 ...
 </programlisting>
 			</example>
@@ -184,7 +188,9 @@ modparam("app_java", "java_options", "-verbose:gc,class,jni -Djava.compiler=NONE
 				<programlisting format="linespecific">
 ...
 # Assumes "application java folder" is located at /opt/kamailio/java
-modparam("app_java", "java_options", "-Xdebug -verbose:gc,class,jni -Djava.compiler=NONE -Djava.class.path=/path/to/kamailio/modules:/opt/kamailio/java:/opt/kamailio/java/kamailio.jar")
+modparam("app_java", "java_options", "-Xdebug -verbose:gc,class,jni 
+    -Djava.compiler=NONE -Djava.class.path=/path/to/kamailio/modules:
+    /opt/kamailio/java:/opt/kamailio/java/kamailio.jar")
 ...
 </programlisting>
 			</example>
@@ -240,7 +246,8 @@ modparam("app_java", "force_cmd_exec", 1)
 		object       L 
 		short        S 
 		void         V 
-		Note that to specify an object, the "L" is followed by the object's class name and ends with a semi-colon, ';' .
+		Note that to specify an object, the "L" is followed by the 
+		object's class name and ends with a semi-colon, ';' .
     			</programlisting>
 			</para>
     		<para> app_java supports the following signatures:
@@ -263,7 +270,8 @@ modparam("app_java", "force_cmd_exec", 1)
 	Parameters are optional, ommitting a parameter meant the passed value is NULL.
 	Parameters count should be exactly the same as signature count.
 	Note 1: Arrays representation (symbol '[') is not supported yet.
-	Note 2: You shall use a correct signature, e.g. the following examples of combinations are invalid:    
+	Note 2: You shall use a correct signature, e.g. the following examples of
+	combinations are invalid:
     	java_method_exec("ExampleMethod", "ZI", "False");
         java_method_exec("ExampleMethod", "LI", "something", "5");
 </programlisting>
@@ -293,8 +301,8 @@ java_method_exec("ExampleMethod", "V");
 # Java
 public int ExampleMethod()
 {
-		... do something;
-		return 1;
+    ... do something;
+    return 1;
 }
 </programlisting>
 			    	</example>
@@ -316,8 +324,8 @@ java_method_exec("ExampleMethod", "Ljava/lang/String;I", "$mb", "$ml");
 # Java
 public int ExampleMethod(String SipMessageBuffer, int SipMessageLenght)
 {
-		... do something with buffer;
-		return 1;
+    ... do something with buffer;
+    return 1;
 }
 </programlisting>
 	    			</example>
@@ -339,11 +347,12 @@ java_method_exec("ExampleMethod", "ZB", "true", "0x05");
 # Java
 public int ExampleMethod(boolean flagSet, byte bFlag);
 {
-		if (flagSet)
-		{
-			... do something with flags;
-		}
-		return 1;
+    if (flagSet)
+    {
+        ... do something with flags;
+    }
+
+    return 1;
 }
 </programlisting>
     				</example>
@@ -373,8 +382,8 @@ java_staticmethod_exec("ExampleMethod", "V");
 # Java
 public static int ExampleMethod()
 {
-		... do something;
-		return 1;
+    ... do something;
+    return 1;
 }
 </programlisting>
     				</example>
@@ -396,8 +405,8 @@ java_staticmethod_exec("ExampleMethod", "Ljava/lang/String;I", "$mb", "$ml");
 # Java
 public static int ExampleMethod(String SipMessageBuffer, int SipMessageLenght)
 {
-		... do something with buffer;
-		return 1;
+    ... do something with buffer;
+    return 1;
 }
 </programlisting>
     				</example>
@@ -419,11 +428,12 @@ java_staticmethod_exec("ExampleMethod", "ZB", "true", "0x05");
 # Java
 public static int ExampleMethod(boolean flagSet, byte bFlag);
 {
-		if (flagSet)
-		{
-			... do something with flags;
-		}
-		return 1;
+    if (flagSet)
+    {
+        ... do something with flags;
+    }
+
+    return 1;
 }
 </programlisting>
     				</example>
@@ -454,8 +464,8 @@ java_s_method_exec("ExampleMethod", "V");
 # Java
 public synchronized int ExampleMethod()
 {
-		... do something;
-		return 1;
+    ... do something;
+    return 1;
 }
 </programlisting>
     				</example>
@@ -477,8 +487,8 @@ java_s_method_exec("ExampleMethod", "Ljava/lang/String;I", "$mb", "$ml");
 # Java
 public synchronized int ExampleMethod(String SipMessageBuffer, int SipMessageLenght)
 {
-		... do something with buffer;
-		return 1;
+    ... do something with buffer;
+    return 1;
 }
 </programlisting>
     				</example>
@@ -500,11 +510,12 @@ java_s_method_exec("ExampleMethod", "ZB", "true", "0x05");
 # Java
 public synchronized int ExampleMethod(boolean flagSet, byte bFlag);
 {
-		if (flagSet)
-		{
-			... do something with flags;
-		}
-		return 1;
+    if (flagSet)
+    {
+        ... do something with flags;
+    }
+
+    return 1;
 }
 </programlisting>
     				</example>
@@ -535,8 +546,8 @@ java_s_staticmethod_exec("ExampleMethod", "V");
 # Java
 public static synchronized int ExampleMethod()
 {
-		... do something;
-		return 1;
+    ... do something;
+    return 1;
 }
 </programlisting>
     				</example>
@@ -558,8 +569,8 @@ java_s_staticmethod_exec("ExampleMethod", "Ljava/lang/String;I", "$mb", "$ml");
 # Java
 public static synchronized int ExampleMethod(String SipMessageBuffer, int SipMessageLenght)
 {
-		... do something with buffer;
-		return 1;
+    ... do something with buffer;
+    return 1;
 }
 </programlisting>
     				</example>
@@ -581,11 +592,12 @@ java_s_staticmethod_exec("ExampleMethod", "ZB", "true", "0x05");
 # Java
 public static synchronized int ExampleMethod(boolean flagSet, byte bFlag);
 {
-		if (flagSet)
-		{
-			... do something with flags;
-		}
-		return 1;
+    if (flagSet)
+    {
+        ... do something with flags;
+    }
+
+    return 1;
 }
 </programlisting>
     				</example>
@@ -616,25 +628,25 @@ import org.siprouter.NativeInterface.*;
 
 public class Kamailio extends NativeMethods
 {
-		/* Here you should specify a full path to app_java.so */
-		static
-		{
-				System.load("/opt/kamailio/lib/kamailio/modules/app_java.so");
-		}
-
-		/* Constructor. Do not remove !!! */
-		public Kamailio()
-		{
-		}
-
-		/*
-		This method should be executed for each children process, immediately after forking.
-		Required. Do not remove !!!
-		*/
-		public int child_init(int rank)
-		{
-				return 1;
-		}
+    /* Here you should specify a full path to app_java.so */
+    static
+    {
+        System.load("/opt/kamailio/lib/kamailio/modules/app_java.so");
+    }
+
+    /* Constructor. Do not remove !!! */
+    public Kamailio()
+    {
+    }
+
+    /*
+        This method should be executed for each children process, immediately after forking.
+	Required. Do not remove !!!
+    */
+    public int child_init(int rank)
+    {
+	return 1;
+    }
 }
 </programlisting>
     		</example>

+ 2 - 0
modules/app_java/global.h

@@ -35,6 +35,8 @@
 
 #include <jni.h>
 
+#define	APP_NAME	"app_java"
+
 JavaVM *jvm;
 JNIEnv *env;
 jclass KamailioClass;

+ 7 - 7
modules/app_java/java_iface.c

@@ -120,19 +120,19 @@ int java_exec(struct sip_msg *msgp, int is_static, int is_synchronized, char *me
 
     if (signature == NULL || !strcmp(signature, ""))
     {
-	LM_ERR("java_method_exec(): signature is empty or invalid.\n");
+	LM_ERR("%s: java_method_exec(): signature is empty or invalid.\n", APP_NAME);
 	return -1;
     }
 
     if (param == NULL && strcmp(signature, "V"))
     {
-	LM_ERR("java_method_exec(): no parameter (parameter is NULL) but signature '%s' is not equals to 'V'.\n", signature);
+	LM_ERR("%s: java_method_exec(): no parameter (parameter is NULL) but signature '%s' is not equals to 'V'.\n", APP_NAME, signature);
 	return -1;
     }
 
     if (is_sig_allowed(signature) == 0)
     {
-	LM_ERR("java_method_exec(): error: signature '%s' isn't supported yet.\n", signature);
+	LM_ERR("%s: java_method_exec(): error: signature '%s' isn't supported yet.\n", APP_NAME, signature);
 	return -1;
     }
 
@@ -147,7 +147,7 @@ int java_exec(struct sip_msg *msgp, int is_static, int is_synchronized, char *me
     cs = (char *)pkg_malloc(cslen * sizeof(char));
     if (!cs)
     {
-	LM_ERR("pkg_malloc() has failed. Can't allocate %lu bytes. Not enough memory!\n", (unsigned long)cslen);
+	LM_ERR("%s: pkg_malloc() has failed. Can't allocate %lu bytes. Not enough memory!\n", APP_NAME, (unsigned long)cslen);
 	return -1;
     }
     snprintf(cs, cslen, "(%s)%s", signature, retval_sig);
@@ -208,7 +208,7 @@ int java_exec(struct sip_msg *msgp, int is_static, int is_synchronized, char *me
 	if ((*env)->MonitorEnter(env, invk_method_ref) != JNI_OK)
         {
 	    locked = 0;
-	    LM_ERR("MonitorEnter() has failed!\n");
+	    LM_ERR("%s: MonitorEnter() has failed! Can't synchronize!\n", APP_NAME);
 	}
 	else
 	{
@@ -240,7 +240,7 @@ int java_exec(struct sip_msg *msgp, int is_static, int is_synchronized, char *me
 
     if ((*env)->ExceptionCheck(env))
     {
-        LM_ERR("%s(): %s() has failed. See exception below.\n", 
+        LM_ERR("%s: %s(): %s() has failed. See exception below.\n", APP_NAME,
 		(is_static ? 
 			(is_synchronized ? "java_s_staticmethod_exec" : "java_staticmethod_exec") :
 			(is_synchronized ? "java_s_method_exec" : "java_method_exec")
@@ -261,7 +261,7 @@ int java_exec(struct sip_msg *msgp, int is_static, int is_synchronized, char *me
     {
 	if ((*env)->MonitorExit(env, invk_method_ref) != JNI_OK)
 	{
-	    LM_ERR("MonitorExit) has failed!\n");
+	    LM_ERR("%s: MonitorExit() has failed! Can't synchronize!\n", APP_NAME);
 	}
     }
 

+ 4 - 4
modules/app_java/java_mod.c

@@ -78,7 +78,7 @@ static cmd_export_t cmds[] = {
 
 /** module exports */
 struct module_exports exports = {
-    "app_java",                     /* module name */
+    APP_NAME,                       /* module name */
 //    RTLD_NOW | RTLD_GLOBAL,         /* dlopen flags */
     DEFAULT_DLFLAGS,		    /* dlopen flags */
     cmds,                           /* exported functions */
@@ -109,7 +109,7 @@ static int mod_init(void)
 
     if (force_cmd_exec)
     {
-	LM_NOTICE("app_java: Parameter force_cmd_exec may cause a memory leaks if used from embedded languages\n");
+	LM_NOTICE("%s: Parameter force_cmd_exec may cause a memory leaks if used from embedded languages\n", APP_NAME);
     }
 
     options = (JavaVMOption *)pkg_malloc(sizeof(JavaVMOption));
@@ -141,7 +141,7 @@ static int mod_init(void)
 	return -1;
     }
 
-    LM_INFO("app_java: Java VM initialization OK\n");
+    LM_INFO("%s: Java VM initialization OK\n", APP_NAME);
 
     // attach to current thread
     (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
@@ -193,7 +193,7 @@ static int mod_init(void)
 	return -1;
     }
 
-    LM_INFO("app_java: module initialization OK\n");
+    LM_INFO("%s: module initialization OK\n", APP_NAME);
 
     if (jvm != NULL)
         (*jvm)->DetachCurrentThread(jvm);

+ 16 - 16
modules/app_java/java_msgobj.c

@@ -50,7 +50,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     SipMsgInstance = (jobject *)pkg_malloc(sizeof(jobject));
     if (!SipMsgInstance)
     {
-	LM_ERR("pkg_malloc() has failed. Not enough memory!\n");
+	LM_ERR("%s: pkg_malloc() has failed. Not enough memory!\n", APP_NAME);
 	return NULL;
     }
     memset(SipMsgInstance, 0, sizeof(jobject));
@@ -83,7 +83,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-	LM_ERR("Can't find symbol org.siprouter.SipMsg.id\n");
+	LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.id\n", APP_NAME);
 
         return NULL;
     }
@@ -99,7 +99,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-	LM_ERR("Can't find symbol org.siprouter.SipMsg.pid\n");
+	LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.pid\n", APP_NAME);
         return NULL;
     }
     (*env)->SetIntField(env, SipMsgInstance, fid, msg->pid);
@@ -114,7 +114,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-	LM_ERR("Can't find symbol org.siprouter.SipMsg.eoh\n");
+	LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.eoh\n", APP_NAME);
         return NULL;
     }
     jStrParam = (*env)->NewStringUTF(env, msg->eoh);
@@ -131,7 +131,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-	LM_ERR("Can't find symbol org.siprouter.SipMsg.unparsed\n");
+	LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.unparsed\n", APP_NAME);
         return NULL;
     }
     jStrParam = (*env)->NewStringUTF(env, msg->unparsed);
@@ -148,7 +148,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.buf\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.buf\n", APP_NAME);
         return NULL;
     }
     jStrParam = (*env)->NewStringUTF(env, msg->buf);
@@ -165,7 +165,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.len\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.len\n", APP_NAME);
         return NULL;
     }
     (*env)->SetIntField(env, SipMsgInstance, fid, msg->len);
@@ -180,7 +180,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.new_uri\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.new_uri\n", APP_NAME);
         return NULL;
     }
     jStrParam = (*env)->NewStringUTF(env, msg->new_uri.len <= 0 ? "" : msg->new_uri.s);
@@ -197,7 +197,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.dst_uri\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.dst_uri\n", APP_NAME);
         return NULL;
     }
     jStrParam = (*env)->NewStringUTF(env, msg->dst_uri.len <= 0 ? "" : msg->dst_uri.s);
@@ -214,7 +214,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.parsed_orig_ruri_ok\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.parsed_orig_ruri_ok\n", APP_NAME);
         return NULL;
     }
     (*env)->SetIntField(env, SipMsgInstance, fid, msg->parsed_orig_ruri_ok);
@@ -229,7 +229,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.add_to_branch_s\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.add_to_branch_s\n", APP_NAME);
         return NULL;
     }
     jStrParam = (*env)->NewStringUTF(env, (msg->add_to_branch_len <= 0 || msg->add_to_branch_s == NULL) ? "" : strdup(msg->add_to_branch_s));
@@ -246,7 +246,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.add_to_branch_len\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.add_to_branch_len\n", APP_NAME);
         return NULL;
     }
     (*env)->SetIntField(env, SipMsgInstance, fid, msg->add_to_branch_len);
@@ -261,7 +261,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.hash_index\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.hash_index\n", APP_NAME);
         return NULL;
     }
     (*env)->SetIntField(env, SipMsgInstance, fid, msg->hash_index);
@@ -276,7 +276,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.msg_flags\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.msg_flags\n", APP_NAME);
         return NULL;
     }
     (*env)->SetIntField(env, SipMsgInstance, fid, msg->msg_flags);
@@ -291,7 +291,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.set_global_address\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.set_global_address\n", APP_NAME);
         return NULL;
     }
     jStrParam = (*env)->NewStringUTF(env, (msg->set_global_address.len <= 0 || msg->set_global_address.s == NULL) ? "" : msg->set_global_address.s);
@@ -308,7 +308,7 @@ jobject *fill_sipmsg_object(JNIEnv *env, struct sip_msg *msg)
     if (!fid)
     {
 	(*env)->ExceptionClear(env);
-        LM_ERR("Can't find symbol org.siprouter.SipMsg.set_global_port\n");
+        LM_ERR("%s: Can't find symbol org.siprouter.SipMsg.set_global_port\n", APP_NAME);
         return NULL;
     }
     jStrParam = (*env)->NewStringUTF(env, (msg->set_global_port.len <= 0 || msg->set_global_port.s == NULL) ? "" : msg->set_global_port.s);

+ 28 - 28
modules/app_java/java_native_methods.c

@@ -304,7 +304,7 @@ JNIEXPORT jint JNICALL Java_org_siprouter_NativeMethods_KamExec(JNIEnv *jenv, jo
 
     if (jfname == NULL)
     {
-	LM_ERR("app_java: KamExec() required at least 1 argument (function name)\n");
+	LM_ERR("%s: KamExec() required at least 1 argument (function name)\n", APP_NAME);
 	return -1;
     }
 
@@ -369,14 +369,14 @@ int KamExec(JNIEnv *jenv, char *fname, int argc, char **argv)
     fexport = find_export_record(fname, argc, 0, &mod_ver);
     if (!fexport)
     {
-	LM_ERR("app_java: KamExec(): '%s' - no such function\n", fname);
+	LM_ERR("%s: KamExec(): '%s' - no such function\n", APP_NAME, fname);
         return -1;
     }
 
     /* check fixups */
     if (force_cmd_exec == 0 && fexport->fixup != NULL && fexport->free_fixup == NULL)
     {
-        LM_ERR("app_java: KamExec(): function '%s' has fixup - cannot be used\n", fname);
+        LM_ERR("%s: KamExec(): function '%s' has fixup - cannot be used\n", APP_NAME, fname);
 	return -1;
     }
 
@@ -391,7 +391,7 @@ int KamExec(JNIEnv *jenv, char *fname, int argc, char **argv)
 	case 6:			mod_type = MODULE6_T;	break;
 	case VAR_PARAM_NO:	mod_type = MODULEX_T;	break;
 	default:
-		LM_ERR("app_java: KamExec(): unknown/bad definition for function '%s' (%d params)\n", fname, fexport->param_no);
+		LM_ERR("%s: KamExec(): unknown/bad definition for function '%s' (%d params)\n", APP_NAME, fname, fexport->param_no);
 		return -1;
     }
 
@@ -409,7 +409,7 @@ int KamExec(JNIEnv *jenv, char *fname, int argc, char **argv)
 
     if (!act)
     {
-	LM_ERR("app_java: KamExec(): action structure couldn't be created\n");
+	LM_ERR("%s: KamExec(): action structure couldn't be created\n", APP_NAME);
 	return -1;
     }
 
@@ -422,7 +422,7 @@ int KamExec(JNIEnv *jenv, char *fname, int argc, char **argv)
             rval = fexport->fixup(0, 0);
             if (rval < 0)
 	    {
-		LM_ERR("app_java: KamExec(): (no params) Error in fixup (0) for '%s'\n", fname);
+		LM_ERR("%s: KamExec(): (no params) Error in fixup (0) for '%s'\n", APP_NAME, fname);
                 return -1;
             }
         }
@@ -435,7 +435,7 @@ int KamExec(JNIEnv *jenv, char *fname, int argc, char **argv)
         	    rval = fexport->fixup(&(act->val[i+2].u.data), i+1);
         	    if (rval < 0)
 		    {
-			LM_ERR("app_java: KamExec(): (params: %d) Error in fixup (%d) for '%s'\n", argc, i+1, fname);
+			LM_ERR("%s: KamExec(): (params: %d) Error in fixup (%d) for '%s'\n", APP_NAME, argc, i+1, fname);
             		return -1;
         	    }
         	    act->val[i+2].type = MODFIXUP_ST;
@@ -538,7 +538,7 @@ JNIEXPORT jstring JNICALL Java_org_siprouter_SipMsg_getStatus(JNIEnv *jenv, jobj
 
     if ((msg->first_line).type != SIP_REQUEST)
     {
-	LM_ERR("app_java: getStatus(): Unable to fetch status. Error: Not a request message - no method available.\n");
+	LM_ERR("%s: getStatus(): Unable to fetch status. Error: Not a request message - no method available.\n", APP_NAME);
         return NULL;
     }
 
@@ -571,7 +571,7 @@ JNIEXPORT jstring JNICALL Java_org_siprouter_SipMsg_getRURI(JNIEnv *jenv, jobjec
 
     if ((msg->first_line).type != SIP_REQUEST)
     {
-	LM_ERR("app_java: getRURI(): Unable to fetch ruri. Error: Not a request message - no method available.\n");
+	LM_ERR("%s: getRURI(): Unable to fetch ruri. Error: Not a request message - no method available.\n", APP_NAME);
         return NULL;
     }
 
@@ -624,7 +624,7 @@ JNIEXPORT jobject JNICALL Java_org_siprouter_SipMsg_getSrcAddress(JNIEnv *jenv,
     ip = ip_addr2a(&msg->rcv.src_ip);
     if (!ip)
     {
-	LM_ERR("app_java: getSrcAddress(): Unable to fetch src ip address.\n");
+	LM_ERR("%s: getSrcAddress(): Unable to fetch src ip address.\n", APP_NAME);
 	return NULL;
     }
     jip = (*jenv)->NewStringUTF(jenv, ip);
@@ -637,7 +637,7 @@ JNIEXPORT jobject JNICALL Java_org_siprouter_SipMsg_getSrcAddress(JNIEnv *jenv,
     port = msg->rcv.src_port;
     if (port == 0x0)
     {
-	LM_ERR("app_java: getSrcAddress(): Unable to fetch src port.\n");
+	LM_ERR("%s: getSrcAddress(): Unable to fetch src port.\n", APP_NAME);
 	return NULL;
     }
 
@@ -689,7 +689,7 @@ JNIEXPORT jobject JNICALL Java_org_siprouter_SipMsg_getDstAddress(JNIEnv *jenv,
     ip = ip_addr2a(&msg->rcv.dst_ip);
     if (!ip)
     {
-	LM_ERR("app_java: getDstAddress(): Unable to fetch src ip address.\n");
+	LM_ERR("%s: getDstAddress(): Unable to fetch src ip address.\n", APP_NAME);
 	return NULL;
     }
     jip = (*jenv)->NewStringUTF(jenv, ip);
@@ -702,7 +702,7 @@ JNIEXPORT jobject JNICALL Java_org_siprouter_SipMsg_getDstAddress(JNIEnv *jenv,
     port = msg->rcv.dst_port;
     if (port == 0x0)
     {
-	LM_ERR("app_java: getDstAddress(): Unable to fetch src port.\n");
+	LM_ERR("%s: getDstAddress(): Unable to fetch src port.\n", APP_NAME);
 	return NULL;
     }
 
@@ -733,7 +733,7 @@ JNIEXPORT jstring JNICALL Java_org_siprouter_SipMsg_getBuffer(JNIEnv *jenv, jobj
 
     if ((msg->first_line).type != SIP_REQUEST)
     {
-	LM_ERR("app_java: getRURI(): Unable to fetch ruri. Error: Not a request message - no method available.\n");
+	LM_ERR("%s: getRURI(): Unable to fetch ruri. Error: Not a request message - no method available.\n", APP_NAME);
         return NULL;
     }
 
@@ -792,7 +792,7 @@ jint cf_seturi(JNIEnv *jenv, jobject this, jstring juri, char *fname)
 
     if (!msg)
     {
-	LM_ERR("app_java: %s: Can't process, msg=NULL\n", fname);
+	LM_ERR("%s: %s: Can't process, msg=NULL\n", APP_NAME, fname);
 	return -1;
     }
 
@@ -833,7 +833,7 @@ JNIEXPORT jint JNICALL Java_org_siprouter_CoreMethods_add_1local_1rport(JNIEnv *
 
     if (!msg)
     {
-	LM_ERR("app_java: add_local_rport: Can't process, msg=NULL\n");
+	LM_ERR("%s: add_local_rport: Can't process, msg=NULL\n", APP_NAME);
 	return -1;
     }
 
@@ -863,7 +863,7 @@ JNIEXPORT jint JNICALL Java_org_siprouter_CoreMethods_append_1branch(JNIEnv *jen
 
     if (!msg)
     {
-	LM_ERR("app_java: append_branch: Can't process, msg=NULL\n");
+	LM_ERR("%s: append_branch: Can't process, msg=NULL\n", APP_NAME);
 	return -1;
     }
 
@@ -916,7 +916,7 @@ JNIEXPORT jint JNICALL Java_org_siprouter_CoreMethods_drop(JNIEnv *jenv, jobject
 
     if (!msg)
     {
-	LM_ERR("app_java: drop: Can't process, msg=NULL\n");
+	LM_ERR("%s: drop: Can't process, msg=NULL\n", APP_NAME);
 	return -1;
     }
 
@@ -962,7 +962,7 @@ jint cf_force_rport(JNIEnv *jenv, jobject this, char *fname)
 
     if (!msg)
     {
-	LM_ERR("app_java: %s: Can't process, msg=NULL\n", fname);
+	LM_ERR("%s: %s: Can't process, msg=NULL\n", APP_NAME, fname);
 	return -1;
     }
 
@@ -992,21 +992,21 @@ JNIEXPORT jint JNICALL Java_org_siprouter_CoreMethods_force_1send_1socket(JNIEnv
 
     if (!msg)
     {
-	LM_ERR("app_java: force_send_socket: Can't process, msg=NULL\n");
+	LM_ERR("%s: force_send_socket: Can't process, msg=NULL\n", APP_NAME);
 	return -1;
     }
 
     nl = (struct name_lst *)pkg_malloc(sizeof(struct name_lst));
     if (!nl)
     {
-	LM_ERR("app_java: force_send_socket: pkg_malloc() has failed. Not enough memory!\n");
+	LM_ERR("%s: force_send_socket: pkg_malloc() has failed. Not enough memory!\n", APP_NAME);
 	return -1;
     }
     
     si = (struct socket_id *)pkg_malloc(sizeof(struct socket_id));
     if (!si)
     {
-	LM_ERR("app_java: force_send_socket: pkg_malloc() has failed. Not enough memory!\n");
+	LM_ERR("%s: force_send_socket: pkg_malloc() has failed. Not enough memory!\n", APP_NAME);
 	return -1;
     }
     
@@ -1060,7 +1060,7 @@ JNIEXPORT jint JNICALL Java_org_siprouter_CoreMethods_forward(JNIEnv *jenv, jobj
 
     if (!msg)
     {
-	LM_ERR("app_java: forward: Can't process, msg=NULL\n");
+	LM_ERR("%s: forward: Can't process, msg=NULL\n", APP_NAME);
 	return -1;
     }
 
@@ -1108,7 +1108,7 @@ JNIEXPORT jboolean JNICALL Java_org_siprouter_CoreMethods_isflagset(JNIEnv *jenv
 {
     if (!msg)
     {
-	LM_ERR("app_java: isflagset: Can't process, msg=NULL\n");
+	LM_ERR("%s: isflagset: Can't process, msg=NULL\n", APP_NAME);
 	return -1;
     }
 
@@ -1126,7 +1126,7 @@ JNIEXPORT void JNICALL Java_org_siprouter_CoreMethods_setflag(JNIEnv *jenv, jobj
 {
     if (!msg)
     {
-	LM_ERR("app_java: setflag: Can't process, msg=NULL\n");
+	LM_ERR("%s: setflag: Can't process, msg=NULL\n", APP_NAME);
 	return;
     }
 
@@ -1144,7 +1144,7 @@ JNIEXPORT void JNICALL Java_org_siprouter_CoreMethods_resetflag(JNIEnv *jenv, jo
 {
     if (!msg)
     {
-	LM_ERR("app_java: resetflag: Can't process, msg=NULL\n");
+	LM_ERR("%s: resetflag: Can't process, msg=NULL\n", APP_NAME);
 	return;
     }
 
@@ -1167,7 +1167,7 @@ JNIEXPORT jint JNICALL Java_org_siprouter_CoreMethods_revert_1uri(JNIEnv *jenv,
 
     if (!msg)
     {
-	LM_ERR("app_java: revert_uri: Can't process, msg=NULL\n");
+	LM_ERR("%s: revert_uri: Can't process, msg=NULL\n", APP_NAME);
 	return -1;
     }
 
@@ -1204,7 +1204,7 @@ JNIEXPORT jint JNICALL Java_org_siprouter_CoreMethods_route(JNIEnv *jenv, jobjec
 
     if (retval == -1)	// route index lookup failed.
     {
-	LM_ERR("app_java: route: failed to find route name '%s'\n", ctarget);
+	LM_ERR("%s: route: failed to find route name '%s'\n", APP_NAME, ctarget);
 	(*jenv)->ReleaseStringUTFChars(jenv, jtarget, ctarget);
 	return -1;
     }

+ 26 - 26
modules/app_java/java_sig_parser.c

@@ -46,7 +46,7 @@ int is_sig_allowed(char *s)
 
     if (!strcmp(s, " ") || !strcmp(s, "\n") || !strcmp(s, "\r") || !strcmp(s, "\t"))
     {
-	LM_ERR("signature error: '%s' contains whitespaces or any unparsable chars.\n", s);
+	LM_ERR("%s: signature error: '%s' contains whitespaces or any unparsable chars.\n", APP_NAME, s);
 	return 0;
     }
 
@@ -56,20 +56,20 @@ int is_sig_allowed(char *s)
     {
 	if (!strcmp(s, "["))		// invalid signature modifier definition
 	{
-	    LM_ERR("signature error: '%s': no type of array specified.\n", s);
+	    LM_ERR("%s: signature error: '%s': no type of array specified.\n", APP_NAME, s);
 	    return 0;
 	}
 
 	if (!strcmp(s, "L"))		// invalid signature modifier definition
 	{
-	    LM_ERR("signature error '%s': no object specified.\n", s);
+	    LM_ERR("%s: signature error '%s': no object specified.\n", APP_NAME, s);
 	    return 0;
 	}
 
 #ifndef JAVA_INV_SUPP_TYPE_VOID
 	if (!strcmp(s, "V"))
 	{
-	    LM_ERR("signature error '%s': no object specified.\n", s);
+	    LM_ERR("%s: signature error '%s': no object specified.\n", APP_NAME, s);
 	    return 0;
 	}
 #endif
@@ -80,7 +80,7 @@ int is_sig_allowed(char *s)
 #ifndef JAVA_INV_SUPP_TYPE_ARRAYS
 	if (strcmp(s, "[") > 0)
 	{
-	    LM_ERR("signature error: '%s' denotes array which isn't supported yet.\n", s);
+	    LM_ERR("%s: signature error: '%s' denotes array which isn't supported yet.\n", APP_NAME, s);
 	    return 0;
 	}
 #endif
@@ -89,7 +89,7 @@ int is_sig_allowed(char *s)
 	if (strrchr(&s[0], 'L') > 0)
 	{
 #ifndef JAVA_INV_SUPP_TYPE_OBJECTS
-	    LM_ERR("signature error: '%s' denotes object which isn't supported yet.\n", s);
+	    LM_ERR("%s: signature error: '%s' denotes object which isn't supported yet.\n", APP_NAME, s);
 	    return 0;
 #else
 	    int f = 0;
@@ -131,7 +131,7 @@ int is_sig_allowed(char *s)
 #endif
 	    if (f == 0)
 	    {
-		LM_ERR("signature '%s' isn't supported yet.\n", s);
+		LM_ERR("%s: signature '%s' isn't supported yet.\n", APP_NAME, s);
 		return 0;
 	    }
 #endif
@@ -184,19 +184,19 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
     ret = (jvalue *)pkg_malloc(sizeof(jvalue));
     if (!ret)
     {
-	LM_ERR("pkg_malloc() has failed. Not enouph memory!\n");
+	LM_ERR("%s: pkg_malloc() has failed. Not enouph memory!\n", APP_NAME);
 	return NULL;
     }
 
     if (sig == NULL || strlen(sig) <= 0)
     {
-	LM_ERR("app_java: Can't process empty or NULL signature.\n");
+	LM_ERR("%s: Can't process empty or NULL signature.\n", APP_NAME);
 	pkg_free(ret);
 	return NULL;
     }
     if (pval == NULL || strlen(pval) <= 0)
     {
-	LM_ERR("app_java: Can't process empty or NULL parameter value.\n");
+	LM_ERR("%s: Can't process empty or NULL parameter value.\n", APP_NAME);
 	pkg_free(ret);
 	return NULL;
     }
@@ -219,7 +219,7 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
 		(*ret).z = (jboolean)JNI_FALSE;
 	    else
 	    {
-    		LM_ERR("app_java: Can't cast '%s' to type '%s'.\n", pval, sig);
+    		LM_ERR("%s: Can't cast '%s' to type '%s'.\n", APP_NAME, pval, sig);
 		pkg_free(ret);
 		return NULL;
 	    }
@@ -237,13 +237,13 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
 	    sscanf(pval, "%x", &siptr);
 	    if (siptr == 0 && errno != 0)
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Error: %s.\n", pval, sig, get_conv_err_str(errno));
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Error: %s.\n", APP_NAME, pval, sig, get_conv_err_str(errno));
 		pkg_free(ret);
                 return NULL;
 	    }
             if (siptr < SCHAR_MAX || siptr > SCHAR_MAX)
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Reason: overflow.", pval, sig);
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Reason: overflow.", APP_NAME, pval, sig);
 		pkg_free(ret);
                 return NULL;
 	    }
@@ -261,13 +261,13 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
 	    sscanf(pval, "%c", &scptr);
 	    if (scptr == 0 && errno != 0)
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Error: %s.\n", pval, sig, get_conv_err_str(errno));
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Error: %s.\n", APP_NAME, pval, sig, get_conv_err_str(errno));
 		pkg_free(ret);
                 return NULL;
 	    }
             if (scptr < CHAR_MIN || scptr > CHAR_MAX)	// overflow
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Reason: overflow.", pval, sig);
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Reason: overflow.", APP_NAME, pval, sig);
 		pkg_free(ret);
                 return NULL;
 	    }
@@ -285,13 +285,13 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
 	    sdptr = (double)strtod(pval, &endptr);
 	    if ((sdptr == 0 && errno != 0) || (pval == endptr))
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Error: %s.\n", pval, sig, get_conv_err_str(errno));
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Error: %s.\n", APP_NAME, pval, sig, get_conv_err_str(errno));
 		pkg_free(ret);
                 return NULL;
 	    }
             if (sdptr < LLONG_MIN || sdptr > LLONG_MAX)	// overflow
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Reason: overflow.", pval, sig);
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Reason: overflow.", APP_NAME, pval, sig);
 		pkg_free(ret);
                 return NULL;
 	    }
@@ -309,13 +309,13 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
 	    sfptr = (float)strtof(pval, &endptr);
 	    if ((sfptr == 0 && errno != 0) || (pval == endptr))
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Error: %s.\n", pval, sig, get_conv_err_str(errno));
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Error: %s.\n", APP_NAME, pval, sig, get_conv_err_str(errno));
 		pkg_free(ret);
                 return NULL;
 	    }
             if (sfptr < FLT_MIN || sfptr > FLT_MAX)	// overflow
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Reason: overflow.", pval, sig);
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Reason: overflow.", APP_NAME, pval, sig);
 		pkg_free(ret);
                 return NULL;
 	    }
@@ -333,13 +333,13 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
 	    slptr = strtol(pval, &endptr, 10);
 	    if ((slptr == 0 && errno != 0) || (pval == endptr))
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Error: %s.\n", pval, sig, get_conv_err_str(errno));
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Error: %s.\n", APP_NAME, pval, sig, get_conv_err_str(errno));
 		pkg_free(ret);
                 return NULL;
 	    }
 	    if (slptr < INT_MIN || slptr > INT_MAX)	// overflow
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Reason: overflow.", pval, sig);
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Reason: overflow.", APP_NAME, pval, sig);
 		pkg_free(ret);
                 return NULL;
 	    }
@@ -357,13 +357,13 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
 	    slptr = (long)strtol(pval, &endptr, 10);
 	    if ((slptr == 0 && errno != 0) || (pval == endptr))
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Error: %s.\n", pval, sig, get_conv_err_str(errno));
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Error: %s.\n", APP_NAME, pval, sig, get_conv_err_str(errno));
 		pkg_free(ret);
                 return NULL;
 	    }
 	    if (slptr < LONG_MIN || slptr > LONG_MAX)	// overflow
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Reason: overflow.", pval, sig);
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Reason: overflow.", APP_NAME, pval, sig);
 		pkg_free(ret);
                 return NULL;
 	    }
@@ -381,13 +381,13 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
 	    ssptr = (short)strtod(pval, &endptr);
 	    if ((ssptr == 0 && errno != 0) || (pval == endptr))
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Error: %s.\n", pval, sig, get_conv_err_str(errno));
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Error: %s.\n", APP_NAME, pval, sig, get_conv_err_str(errno));
 		pkg_free(ret);
                 return NULL;
 	    }
 	    if (ssptr < SHRT_MIN || ssptr > SHRT_MAX)	// overflow
 	    {
-		LM_ERR("app_java: Can't cast '%s' to type '%s'. Reason: overflow.", pval, sig);
+		LM_ERR("%s: Can't cast '%s' to type '%s'. Reason: overflow.", APP_NAME, pval, sig);
 		pkg_free(ret);
                 return NULL;
 	    }
@@ -429,7 +429,7 @@ jvalue *get_value_by_sig_type(char *sig, char *pval)
     else
     {
 	// unknown sig
-	LM_ERR("app_java: Can't cast '%s' to signature '%s'\n", pval, sig);
+	LM_ERR("%s: Can't cast '%s' to signature '%s'\n", APP_NAME, pval, sig);
 	pkg_free(ret);
 	return NULL;
     }

+ 8 - 8
modules/app_java/java_support.c

@@ -171,7 +171,7 @@ void handle_exception(void)
 	(*env)->DeleteLocalRef(env, exception);
     }
 
-    LM_ERR("Exception:\n%s\n", error_msg == NULL ? "(no info)" : error_msg);
+    LM_ERR("%s: Exception:\n%s\n", APP_NAME, error_msg == NULL ? "(no info)" : error_msg);
 
 }
 
@@ -194,25 +194,25 @@ void handle_VM_init_failure(int res)
     switch(res)
     {
 	    case -1:
-		LM_ERR("Couldn't initialize Java VM: unknown error\n");
+		LM_ERR("%s: Couldn't initialize Java VM: unknown error\n", APP_NAME);
 		break;
 	    case -2:
-		LM_ERR("Couldn't initialize Java VM: thread detached from the VM\n");
+		LM_ERR("%s: Couldn't initialize Java VM: thread detached from the VM\n", APP_NAME);
 	        break;
 	    case -3:
-		LM_ERR("Couldn't initialize Java VM: JNI version error\n");
+		LM_ERR("%s: Couldn't initialize Java VM: JNI version error\n", APP_NAME);
 		break;
 	    case -4:
-		LM_ERR("Couldn't initialize Java VM: not enough memory\n");
+		LM_ERR("%s: Couldn't initialize Java VM: not enough memory\n", APP_NAME);
 		break;
 	    case -5:
-		LM_ERR("Couldn't initialize Java VM: VM already created\n");
+		LM_ERR("%s: Couldn't initialize Java VM: VM already created\n", APP_NAME);
 		break;
 	    case -6:
-		LM_ERR("Couldn't initialize Java VM: invalid arguments\n");
+		LM_ERR("%s: Couldn't initialize Java VM: invalid arguments\n", APP_NAME);
 		break;
 	    default:
-		LM_ERR("Couldn't initialize Java VM. Error code: %d\n", res);
+		LM_ERR("%s: Couldn't initialize Java VM. Error code: %d\n", APP_NAME, res);
 		break;
     }
 }

+ 2 - 2
modules/app_java/utils.c

@@ -49,7 +49,7 @@ char **split(char *str, char *sep)
     buf = (char **)pkg_malloc(sizeof(char *));
     if (!buf)
     {
-	LM_ERR("pkg_malloc() has failed. Not enough memory!\n");
+	LM_ERR("%s: pkg_malloc() has failed. Not enough memory!\n", APP_NAME);
 	return NULL;
     }
     memset(&buf, 0, sizeof(char *));
@@ -75,7 +75,7 @@ char **split(char *str, char *sep)
 	buf = (char **)pkg_realloc(buf, (i+1) * sizeof(char *));
 	if (!buf)
 	{
-	    LM_ERR("pkg_realloc() has failed. Not enough memory!\n");
+	    LM_ERR("%s: pkg_realloc() has failed. Not enough memory!\n", APP_NAME);
 	    return NULL;
 	}
         buf[i] = strdup(token);

+ 24 - 8
modules/app_perl/README

@@ -28,6 +28,7 @@ Bastian Friedrich
               5.1. filename (string)
               5.2. modpath (string)
               5.3. reset_cycles (int)
+              5.4. perl_destroy_func (string)
 
         6. Functions
 
@@ -194,10 +195,11 @@ Bastian Friedrich
    1.1. Set filename parameter
    1.2. Set modpath parameter
    1.3. Set reset_cycles parameter
-   1.4. perl_exec_simple() usage
-   1.5. perl_exec() usage
-   1.6. app_perl.set_reset_cycles usage
-   1.7. app_perl.get_reset_cycles usage
+   1.4. Set perl_destroy_func parameter
+   1.5. perl_exec_simple() usage
+   1.6. perl_exec() usage
+   1.7. app_perl.set_reset_cycles usage
+   1.8. app_perl.get_reset_cycles usage
 
 Chapter 1. Admin Guide
 
@@ -216,6 +218,7 @@ Chapter 1. Admin Guide
         5.1. filename (string)
         5.2. modpath (string)
         5.3. reset_cycles (int)
+        5.4. perl_destroy_func (string)
 
    6. Functions
 
@@ -335,6 +338,7 @@ if (perl_exec("ldap_alias")) {
    5.1. filename (string)
    5.2. modpath (string)
    5.3. reset_cycles (int)
+   5.4. perl_destroy_func (string)
 
 5.1. filename (string)
 
@@ -383,6 +387,18 @@ modparam("app_perl", "modpath", "/usr/local/lib/kamailio/perl/")
 modparam("app_perl", "reset_cycles", 100000)
 ...
 
+5.4. perl_destroy_func (string)
+
+   The name of Perl function to be executed before the interpreter is
+   re-initialized (reset -- see reset_cycles parameter) at runtime. This
+   could be useful to clean global variables or file descriptors from the
+   Perl script.
+
+   Example 1.4. Set perl_destroy_func parameter
+...
+modparam("app_perl", "perl_destroy_func", "my_perl_destroy")
+...
+
 6. Functions
 
    6.1. perl_exec_simple(func, [param])
@@ -401,7 +417,7 @@ modparam("app_perl", "reset_cycles", 100000)
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE and BRANCH_ROUTE.
 
-   Example 1.4. perl_exec_simple() usage
+   Example 1.5. perl_exec_simple() usage
 ...
 if (method=="INVITE") {
         perl_exec_simple("dosomething", "on invite messages");
@@ -420,7 +436,7 @@ if (method=="INVITE") {
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE and BRANCH_ROUTE.
 
-   Example 1.5. perl_exec() usage
+   Example 1.6. perl_exec() usage
 ...
 if (perl_exec("ldapalias")) {
         ...
@@ -437,7 +453,7 @@ if (perl_exec("ldapalias")) {
    Set the value of the reset_cycle. The command has one integer
    parameter.
 
-   Example 1.6. app_perl.set_reset_cycles usage
+   Example 1.7. app_perl.set_reset_cycles usage
 ...
 kamcmd app_perl.set_reset_cycles 20000
 ...
@@ -446,7 +462,7 @@ kamcmd app_perl.set_reset_cycles 20000
 
    Return the value of the reset_cycle.
 
-   Example 1.7. app_perl.get_reset_cycles usage
+   Example 1.8. app_perl.get_reset_cycles usage
 ...
 kamcmd app_perl.get_reset_cycles
 ...

+ 9 - 0
modules/app_perl/app_perl_mod.c

@@ -62,6 +62,10 @@ char *filename = NULL;
  * installed */
 char *modpath = NULL;
 
+/* Function to be called before perl interpreter instance is destroyed
+ * when attempting reinit */
+static char *perl_destroy_func = NULL;
+
 /* Allow unsafe module functions - functions with fixups. This will create
  * memory leaks, the variable thus is not documented! */
 int unsafemodfnc = 0;
@@ -128,6 +132,7 @@ static param_export_t params[] = {
 	{"modpath", STR_PARAM, &modpath},
 	{"unsafemodfnc", INT_PARAM, &unsafemodfnc},
 	{"reset_cycles", INT_PARAM, &_ap_reset_cycles_init},
+	{"perl_destroy_func",  STR_PARAM, &perl_destroy_func},
 	{ 0, 0, 0 }
 };
 
@@ -426,6 +431,7 @@ int app_perl_reset_interpreter(void)
 {
 	struct timeval t1;
 	struct timeval t2;
+	char *args[] = { NULL };
 
 	if(*_ap_reset_cycles==0)
 		return 0;
@@ -437,6 +443,9 @@ int app_perl_reset_interpreter(void)
 	if(_ap_exec_cycles<=*_ap_reset_cycles)
 		return 0;
 
+	if(perl_destroy_func)
+		call_argv(perl_destroy_func, G_DISCARD | G_NOARGS, args);
+
 	gettimeofday(&t1, NULL);
 	if (perl_reload()<0) {
 		LM_ERR("perl interpreter cannot be reset [%d/%d]\n",

+ 19 - 0
modules/app_perl/doc/app_perl_admin.xml

@@ -220,6 +220,25 @@ modparam("app_perl", "reset_cycles", 100000)
 </programlisting>
 			</example>
 		</section>
+
+		<section id="app_perl.p.perl_destroy_func">
+			<title><varname>perl_destroy_func</varname> (string)</title>
+			<para>
+			The name of Perl function to be executed before the interpreter is
+			re-initialized (reset -- see reset_cycles parameter) at runtime.
+			This could be useful to clean global variables or file descriptors
+			from the Perl script.
+			</para>
+			<example>
+			<title>Set <varname>perl_destroy_func</varname> parameter</title>
+			<programlisting format="linespecific">
+...
+modparam("app_perl", "perl_destroy_func", "my_perl_destroy")
+...
+</programlisting>
+			</example>
+		</section>
+
 	</section>
 
 	<section>

+ 19 - 9
modules/auth_db/authorize.c

@@ -223,10 +223,10 @@ static int generate_avps(struct sip_msg* msg, db1_res_t* db_res)
 
 
 /*
- * Authorize digest credentials
+ * Authorize digest credentials and set the pointer to used hdr
  */
-static int digest_authenticate(struct sip_msg* msg, str *realm,
-				str *table, hdr_types_t hftype, str *method)
+static int digest_authenticate_hdr(sip_msg_t* msg, str *realm,
+				str *table, hdr_types_t hftype, str *method, hdr_field_t **ahdr)
 {
 	char ha1[256];
 	int res;
@@ -277,6 +277,7 @@ static int digest_authenticate(struct sip_msg* msg, str *realm,
 	}
 
 	cred = (auth_body_t*)h->parsed;
+	if(ahdr!=NULL) *ahdr = h;
 
 	res = get_ha1(&cred->digest.username, realm, table, ha1, &result);
 	if (res < 0) {
@@ -315,6 +316,15 @@ end:
 	return ret;
 }
 
+/*
+ * Authorize digest credentials
+ */
+static int digest_authenticate(sip_msg_t* msg, str *realm,
+				str *table, hdr_types_t hftype, str *method)
+{
+	return digest_authenticate_hdr(msg, realm, table, hftype, method, NULL);
+}
+
 
 /*
  * Authenticate using Proxy-Authorize header field
@@ -475,15 +485,15 @@ int auth_check(struct sip_msg* _m, char* _realm, char* _table, char *_flags)
 	LM_DBG("realm [%.*s] table [%.*s] flags [%d]\n", srealm.len, srealm.s,
 			stable.len,  stable.s, iflags);
 
+	hdr = NULL;
 	if(_m->REQ_METHOD==METHOD_REGISTER)
-		ret = digest_authenticate(_m, &srealm, &stable, HDR_AUTHORIZATION_T,
-						&_m->first_line.u.request.method);
+		ret = digest_authenticate_hdr(_m, &srealm, &stable, HDR_AUTHORIZATION_T,
+						&_m->first_line.u.request.method, &hdr);
 	else
-		ret = digest_authenticate(_m, &srealm, &stable, HDR_PROXYAUTH_T,
-						&_m->first_line.u.request.method);
+		ret = digest_authenticate_hdr(_m, &srealm, &stable, HDR_PROXYAUTH_T,
+						&_m->first_line.u.request.method, &hdr);
 
-	if(ret==AUTH_OK && (iflags&AUTH_CHECK_ID_F)) {
-		hdr = (_m->proxy_auth==0)?_m->authorization:_m->proxy_auth;
+	if(ret==AUTH_OK && hdr!=NULL && (iflags&AUTH_CHECK_ID_F)) {
 		srealm = ((auth_body_t*)(hdr->parsed))->digest.username.user;
 			
 		if((furi=parse_from_uri(_m))==NULL)

+ 141 - 0
modules/cdp/cdp_rpc.c

@@ -0,0 +1,141 @@
+/*
+ * rpc.c
+ *
+ *  Created on: 27 May 2014
+ *      Author: jaybeepee
+ */
+#include "cdp_rpc.h"
+#include "peermanager.h"
+#include "peerstatemachine.h"
+#include "receiver.h"
+#include "../../str.h"
+#include "../../dprint.h"
+
+extern dp_config *config;
+extern peer_list_t *peer_list;
+extern gen_lock_t *peer_list_lock;
+extern char *dp_states[];
+
+static const char* cdp_rpc_disable_peer_doc[2] 	= 	{"disable diameter peer", 0 };
+static const char* cdp_rpc_enable_peer_doc[2] 	= 	{"enable diameter peer", 0 };
+static const char* cdp_rpc_list_peers_doc[2] 	= 	{"list diameter peers and their state", 0 };
+
+static void cdp_rpc_enable_peer(rpc_t* rpc, void* ctx)
+{
+	peer *cdp_peer;
+	str peer_fqdn;
+
+	if (rpc->scan(ctx, "S", &peer_fqdn) < 1) {
+		rpc->fault(ctx, 400, "required peer fqdn argument");
+		return;
+	}
+
+	cdp_peer = get_peer_by_fqdn(&peer_fqdn);
+	if (cdp_peer != NULL) {
+		LM_DBG("Enabling CDP Peer: [%.*s]\n", peer_fqdn.len, peer_fqdn.s);
+		cdp_peer->disabled = 0;
+		return;
+	}
+	rpc->fault(ctx, 400, "peer not found");
+	return;
+}
+
+static void cdp_rpc_disable_peer(rpc_t* rpc, void* ctx)
+{
+	peer *cdp_peer;
+	str peer_fqdn;
+
+	if (rpc->scan(ctx, "S", &peer_fqdn) < 1) {
+		rpc->fault(ctx, 400, "required peer fqdn argument");
+		return;
+	}
+	cdp_peer = get_peer_by_fqdn(&peer_fqdn);
+	if (cdp_peer != NULL) {
+		LM_DBG("Disabling CDP peer: [%.*s]\n", peer_fqdn.len, peer_fqdn.s);
+		cdp_peer->disabled = 1;
+		return;
+	}
+
+	rpc->fault(ctx, 400, "peer not found");
+	return;
+
+}
+
+static void cdp_rpc_list_peers(rpc_t* rpc, void* ctx)
+{
+    void *peers_header;
+    void *peers_container;
+    void *peerdetail_container;
+    void *peerapplication_container;
+    peer *i, *j;
+    int c;
+    char buf[100];
+
+    if (rpc->add(ctx, "{", &peers_header) < 0) {
+            rpc->fault(ctx, 500, "Internal error creating top rpc");
+            return;
+    }
+
+    if (rpc->struct_add(peers_header, "SSddddddd{",
+                            "Realm", &config->realm,
+                            "Identity", &config->identity,
+                            "Accept unknown peers", config->accept_unknown_peers,
+                            "Connect timeout", config->connect_timeout,
+                            "Transaction timeout", config->transaction_timeout,
+                            "Default auth session timeout", config->default_auth_session_timeout,
+                            "Queue length", config->queue_length,
+                            "Workers", config->workers,
+                            "Peer count", config->peers_cnt,
+                            "Peers", &peers_container) < 0) {
+            rpc->fault(ctx, 500, "Internal error creating peers header struct");
+            return;
+    }
+
+    lock_get(peer_list_lock);
+    i = peer_list->head;
+    while (i) {
+    		lock_get(i->lock);
+    		if (rpc->struct_add(peers_container, "S{",
+                            "FQDN", &i->fqdn,
+                            "Details", &peerdetail_container) < 0) {
+                    rpc->fault(ctx, 500, "Internal error creating peers container struct");
+                    lock_release(i->lock);
+                    return;
+            }
+            if (rpc->struct_add(peerdetail_container, "ssd",
+                    "State", dp_states[(int)i->state],
+                    "Disabled", i->disabled?"True":"False",
+            		"Last used", i->last_selected) < 0) {
+                    rpc->fault(ctx, 500, "Internal error creating peer detail container struct");
+                    lock_release(i->lock);
+                    return;
+            }
+            if (rpc->struct_add(peerdetail_container, "{", "Applications", &peerapplication_container) < 0) {
+            	rpc->fault(ctx, 500, "Internal error creating peer application container struct");
+            	lock_release(i->lock);
+            	return;
+            }
+
+            for (c = 0; c < i->applications_cnt; c++) {
+            	snprintf(buf, 100, "%d:%d", i->applications[c].id, i->applications[c].vendor);
+            	if (rpc->struct_add(peerapplication_container, "s",
+						"appid:vendorid", buf) < 0) {
+					rpc->fault(ctx, 500, "Internal error creating appid/vendorid information");
+					lock_release(i->lock);
+					return;
+				}
+            }
+            j=i;
+            i = i->next;
+            lock_release(j->lock);
+    }
+    lock_release(peer_list_lock);
+}
+
+rpc_export_t cdp_rpc[] = {
+	{"cdp.disable_peer",	cdp_rpc_disable_peer,   cdp_rpc_disable_peer_doc,   0},
+	{"cdp.enable_peer",   	cdp_rpc_enable_peer,   	cdp_rpc_enable_peer_doc,   	0},
+	{"cdp.list_peers",   	cdp_rpc_list_peers,   	cdp_rpc_list_peers_doc,   	0},
+	{0, 0, 0, 0}
+};
+

+ 16 - 0
modules/cdp/cdp_rpc.h

@@ -0,0 +1,16 @@
+/*
+ * rpc.h
+ *
+ *  Created on: 27 May 2014
+ *      Author: jaybeepee
+ */
+
+#ifndef RPC_H_
+#define RPC_H_
+
+#include "../../rpc.h"
+#include "config.h"
+
+extern rpc_export_t cdp_rpc[];
+
+#endif /* RPC_H_ */

+ 1 - 1
modules/cdp/config.h

@@ -124,7 +124,7 @@ typedef struct {
 	acceptor_config *acceptors;	/**< list of acceptors */
 	int acceptors_cnt;			/**< size of the list of acceptors */
 	
-	app_config *applications;	/**< list of supporter applications */
+	app_config *applications;	/**< list of supported applications */
 	int applications_cnt;		/**< size of list of supported applications*/
 
 	int *supported_vendors;		/**< list of supported vendor ids */

+ 2 - 1
modules/cdp/diameter_comm.c

@@ -138,6 +138,7 @@ AAAReturnCode AAASendMessage(
 		LM_ERR("AAASendMessage(): Can't find a suitable connected peer in the routing table.\n");
 		goto error;
 	}
+	LM_DBG("Found diameter peer [%.*s] from routing table\n", p->fqdn.len, p->fqdn.s);
 	if (p->state!=I_Open && p->state!=R_Open){
 		LM_ERR("AAASendMessage(): Peer not connected to %.*s\n",p->fqdn.len,p->fqdn.s);
 		goto error;
@@ -194,7 +195,7 @@ AAAReturnCode AAASendMessageToPeer(
 			LM_ERR("AAASendMessageToPeer(): can't add transaction callback for answer.\n");
 	}
 
-//	if (!peer_send_msg(p,message))
+	p->last_selected = time(NULL);
 	if (!sm_process(p,Send_Message,message,0,0))
 		goto error;
 

+ 21 - 3
modules/cdp/doc/cdp_admin.xml

@@ -15,9 +15,9 @@
     <para>CDP (C Diameter Peer) allows Diameter communication to and from
     Kamailio. Most of the code is inherited from DISC
     http://developer.berlios.de/projects/disc/ and OpenIMS and modified for
-    use within Kamailio. A few improvements and new functionality has been added
-    along the way, for example, threshold reporting on Diameter calls that are
-    serviced above a certain threshold.</para>
+    use within Kamailio. A few improvements and new functionality has been
+    added along the way, for example, threshold reporting on Diameter calls
+    that are serviced above a certain threshold.</para>
   </section>
 
   <section>
@@ -137,6 +137,24 @@ modparam("cdp", "latency_threshold", 1000)
     </section>
   </section>
 
+  <section>
+    <title>RPC Commands</title>
+
+    <para>exported RPC commands.</para>
+
+    <section>
+      <title>cdp.disable_peer</title>
+
+      <para>instantly disable a particular diameter peer.</para>
+    </section>
+
+    <section>
+      <title>cdp.enable_peer</title>
+
+      <para>enabe/re-enable a diameter peer</para>
+    </section>
+  </section>
+
   <section>
     <title>Configuration Examples</title>
 

+ 7 - 1
modules/cdp/mod.c

@@ -51,7 +51,9 @@
 #include "diameter_peer.h"
 #include "config.h"
 #include "cdp_load.h"
-
+#include "cdp_rpc.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
 #include "../../cfg/cfg_struct.h"
 
 MODULE_VERSION
@@ -198,6 +200,10 @@ struct module_exports exports = {
  */
 static int cdp_init( void )
 {
+	if (rpc_register_array(cdp_rpc) != 0) {
+		LM_ERR("failed to register RPC commands for CDP module\n");
+		return -1;
+	}
 #ifdef STATISTICS
 	/* register statistics */
 	if ( register_stat("cdp", "replies_response_time", &replies_response_time,0 )!=0 ) {

+ 2 - 0
modules/cdp/peer.h

@@ -111,7 +111,9 @@ typedef struct _peer_t{
 	int R_sock;				/**< socket used as receiver */
 	
 	time_t activity;		/**< timestamp of last activity */
+	time_t last_selected;	/**< timestamp this peer was last selected for routing - used in least recently used load balancing across metric */
 	int is_dynamic;			/**< whether this peer was accepted although it was not initially configured */
+	int disabled;			/**< administratively enable/disable a peer - ie remove/re-add from service dynamically */
 	int waitingDWA;			/**< if a Diameter Watch-dog Request was sent out and waiting for an answer */
 	
 	str send_pipe_name;		/**< pipe to signal messages to be sent out*/

+ 16 - 2
modules/cdp/peermanager.c

@@ -264,6 +264,16 @@ int peer_timer(time_t now,void *ptr)
 	while(p){
 		lock_get(p->lock);
 		n = p->next;
+
+		if (p->disabled && (p->state != Closed || p->state != Closing)) {
+			LM_DBG("Peer [%.*s] has been disabled - shutting down\n", p->fqdn.len, p->fqdn.s);
+			if (p->state == I_Open) sm_process(p, Stop, 0, 1, p->I_sock);
+			if (p->state == R_Open) sm_process(p, Stop, 0, 1, p->R_sock);
+			lock_release(p->lock);
+			p = n;
+			continue;
+		}
+
 		if (p->activity+config->tc<=now){
 			LM_INFO("peer_timer(): Peer %.*s \tState %d \n",p->fqdn.len,p->fqdn.s,p->state);
 			switch (p->state){
@@ -274,8 +284,10 @@ int peer_timer(time_t now,void *ptr)
 						free_peer(p,1);
 						break;
 					}
-					touch_peer(p);
-					sm_process(p,Start,0,1,0);
+					if (!p->disabled) {
+						touch_peer(p);
+						sm_process(p,Start,0,1,0);
+					}
 					break;
 				/* timeouts */
 				case Wait_Conn_Ack:
@@ -293,10 +305,12 @@ int peer_timer(time_t now,void *ptr)
 						p->waitingDWA = 0;
 						if (p->state==I_Open) sm_process(p,I_Peer_Disc,0,1,p->I_sock);
 						if (p->state==R_Open) sm_process(p,R_Peer_Disc,0,1,p->R_sock);
+						LM_WARN("Inactivity on peer [%.*s] and no DWA, Closing peer...\n", p->fqdn.len, p->fqdn.s);
 					} else {
 						p->waitingDWA = 1;
 						Snd_DWR(p);
 						touch_peer(p);
+						LM_WARN("Inactivity on peer [%.*s], sending DWR... - if we don't get a reply, the peer will be closed\n", p->fqdn.len, p->fqdn.s);
 					}
 					break;
 				/* ignored states */

+ 42 - 4
modules/cdp/routing.c

@@ -48,7 +48,10 @@
 #include "peermanager.h"
 #include "diameter_api.h"
 
+#define LB_MAX_PEERS 20			/**< maximum peers that can be loadbalanced accross i.e. same metric */
+
 extern dp_config *config;		/**< Configuration for this diameter peer 	*/
+int gcount = 0;
 
 /**
  * Returns if the peer advertised support for an Application ID
@@ -74,11 +77,19 @@ int peer_handles_application(peer *p,int app_id,int vendor_id)
  */
 peer* get_first_connected_route(routing_entry *r,int app_id,int vendor_id)
 {
+	peer *peers[LB_MAX_PEERS];
+	int peer_count=0;
+	int prev_metric=0;
 	routing_entry *i;
 	peer *p;
+	int j;
+	time_t least_recent_time;
+
 	LM_DBG("get_first_connected_route in list %p for app_id %d and vendor_id %d\n",
 		r,app_id,vendor_id);
 	for(i=r;i;i=i->next){
+		if (peer_count >= LB_MAX_PEERS)
+			break;
 		p = get_peer_by_fqdn(&(i->fqdn));
 		if (!p)
 			LM_DBG("The peer %.*s does not seem to be connected or configured\n",
@@ -86,12 +97,33 @@ peer* get_first_connected_route(routing_entry *r,int app_id,int vendor_id)
 		else
 			LM_DBG("The peer %.*s state is %s\n",i->fqdn.len,i->fqdn.s,
 				(p->state==I_Open||p->state==R_Open)?"opened":"closed");
-		if (p && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) {			
+		if (p && !p->disabled && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) {
 			LM_DBG("The peer %.*s matches - will forward there\n",i->fqdn.len,i->fqdn.s);
-			return p;
+			if (peer_count!=0) {//check the metric
+				if (i->metric != prev_metric)
+					break;
+				//metric must be the same
+				peers[peer_count++] = p;
+			} else {//we're first
+				prev_metric = i->metric;
+				peers[peer_count++] = p;
+			}
 		}
 	}
-	return 0;
+
+	if (peer_count==0)
+		return 0;
+
+	least_recent_time = peers[0]->last_selected;
+	p = peers[0];
+	for (j=1; j<peer_count; j++) {
+		if (peers[j]->last_selected < least_recent_time) {
+			least_recent_time = peers[j]->last_selected;
+			p = peers[j];
+		}
+	}
+
+	return p;
 }
 
 /**
@@ -112,6 +144,8 @@ peer* get_routing_peer(AAAMessage *m)
 	routing_realm *rr;
 	int app_id=0,vendor_id=0;
 	
+	LM_DBG("getting diameter routing peer for realm: [%.*s]\n", m->dest_realm->data.len, m->dest_realm->data.s);
+
 	app_id = m->applicationId;	
 	avp = AAAFindMatchingAVP(m,0,AVP_Vendor_Specific_Application_Id,0,AAA_FORWARD_SEARCH);
 	if (avp){
@@ -151,7 +185,10 @@ peer* get_routing_peer(AAAMessage *m)
 	if (destination_host.len){
 		/* There is a destination host present in the message try and route directly there */
 		p = get_peer_by_fqdn(&destination_host);
-		if (p && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) return p;
+		if (p && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) {
+			p->last_selected = time(NULL);
+			return p;
+		}
 		/* the destination host peer is not connected at the moment, try a normal route then */
 	}
 	
@@ -178,6 +215,7 @@ peer* get_routing_peer(AAAMessage *m)
 	}
 	/* if not found in the realms or no destination_realm, 
 	 * get the first connected host in default routes */
+	LM_DBG("no routing peer found, trying default route\n");
 	p = get_first_connected_route(config->r_table->routes,app_id,vendor_id);
 	if (!p){
 		LM_ERR("get_routing_peer(): No connected DefaultRoute peer found for app_id %d and vendor id %d.\n",

+ 5 - 0
modules/cnxcc/cnxcc_mod.c

@@ -1460,6 +1460,11 @@ static int set_max_credit(struct sip_msg* msg,
 			return -1;
 		}
 
+		if (credit < cost_per_second) {
+			LM_ERR("Not enough credit to start the call: credit=[%f] < cost_per_sec=[%f]", credit, cost_per_second);
+			return -1;
+		}
+
 		if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
 		{
 			LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);

+ 1 - 0
modules/db_flatstore/km_flat_con.c

@@ -115,6 +115,7 @@ struct flat_con* flat_new_connection(struct flat_id* id)
 	fn = get_name(id);
 	if (fn==0){
 		LM_ERR("get_name() failed\n");
+		pkg_free(res);
 		return 0;
 	}
 

+ 2 - 2
modules/db_mysql/km_dbase.c

@@ -258,7 +258,7 @@ static int db_mysql_store_result(const db1_con_t* _h, db1_res_t** _r)
 		db_mysql_free_result(_h, *_r);
 		*_r = 0;
 #if (MYSQL_VERSION_ID >= 40100)
-		while( mysql_more_results(CON_CONNECTION(_h)) && mysql_next_result(CON_CONNECTION(_h)) > 0 ) {
+		while( mysql_more_results(CON_CONNECTION(_h)) && mysql_next_result(CON_CONNECTION(_h)) == 0 ) {
 			MYSQL_RES *res = mysql_store_result( CON_CONNECTION(_h) );
 			mysql_free_result(res);
 		}
@@ -268,7 +268,7 @@ static int db_mysql_store_result(const db1_con_t* _h, db1_res_t** _r)
 
 done:
 #if (MYSQL_VERSION_ID >= 40100)
-	while( mysql_more_results(CON_CONNECTION(_h)) && mysql_next_result(CON_CONNECTION(_h)) > 0 ) {
+	while( mysql_more_results(CON_CONNECTION(_h)) && mysql_next_result(CON_CONNECTION(_h)) == 0 ) {
 		MYSQL_RES *res = mysql_store_result( CON_CONNECTION(_h) );
 		mysql_free_result(res);
 	}

+ 4 - 3
modules/dialog/dlg_handlers.c

@@ -544,6 +544,8 @@ static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param)
 		LM_DBG("dialog %p failed (negative reply)\n", dlg);
 		/* dialog setup not completed (3456XX) */
 		run_dlg_callbacks( DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
+		if(dlg_wait_ack==1)
+			dlg_set_tm_waitack(t, dlg);
 		/* do unref */
 		if (unref)
 			dlg_unref(dlg, unref);
@@ -552,8 +554,6 @@ static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param)
 
 		if_update_stat(dlg_enable_stats, failed_dlgs, 1);
 
-		if(dlg_wait_ack==1)
-			dlg_set_tm_waitack(t, dlg);
 		goto done;
 	}
 
@@ -1356,7 +1356,8 @@ void dlg_ontimeout(struct dlg_tl *tl)
 
 		if(dlg->iflags&DLG_IFLAG_TIMEOUTBYE)
 		{
-			dlg_bye_all(dlg, NULL);
+			if(dlg_bye_all(dlg, NULL)<0)
+				dlg_unref(dlg, 1);
 			/* run event route for end of dlg */
 			dlg_run_event_route(dlg, NULL, dlg->state, DLG_STATE_DELETED);
 			dlg_unref(dlg, 1);

+ 1 - 1
modules/dispatcher/dispatch.c

@@ -227,7 +227,7 @@ int ds_set_attrs(ds_dest_t *dest, str *attrs)
 				&& strncasecmp(pit->name.s, "duid", 4)==0) {
 			dest->attrs.duid = pit->body;
 		} else if(pit->name.len==6
-				&& strncasecmp(pit->name.s, "weight", 4)==0) {
+				&& strncasecmp(pit->name.s, "weight", 6)==0) {
 			str2sint(&pit->body, &dest->attrs.weight);
 		} else if(pit->name.len==7
 				&& strncasecmp(pit->name.s, "maxload", 7)==0) {

+ 50 - 13
modules/dispatcher/dispatcher.c

@@ -545,23 +545,39 @@ static void destroy(void)
 
 }
 
+#define GET_VALUE(param_name,param,i_value,s_value,value_flags) do{ \
+	if(get_is_fparam(&(i_value), &(s_value), msg, (fparam_t*)(param), &(value_flags))!=0) { \
+		LM_ERR("no %s value\n", (param_name)); \
+		return -1; \
+	} \
+}while(0)
+
 /**
  *
  */
 static int w_ds_select(struct sip_msg* msg, char* set, char* alg, int mode)
 {
+	unsigned int algo_flags, set_flags;
+	str s_algo = {NULL, 0};
+	str s_set = {NULL, 0};
 	int a, s;
 	if(msg==NULL)
 		return -1;
 
-	if(fixup_get_ivalue(msg, (gparam_p)set, &s)!=0)
-	{
-		LM_ERR("no dst set value\n");
+	GET_VALUE("destination set", set, s, s_set, set_flags);
+	if (!(set_flags&PARAM_INT)) {
+		if (set_flags&PARAM_STR)
+			LM_ERR("unable to get destination set from [%.*s]\n", s_set.len, s_set.s);
+		else
+			LM_ERR("unable to get destination set\n");
 		return -1;
 	}
-	if(fixup_get_ivalue(msg, (gparam_p)alg, &a)!=0)
-	{
-		LM_ERR("no alg value\n");
+	GET_VALUE("algorithm", alg, a, s_algo, algo_flags);
+	if (!(algo_flags&PARAM_INT)) {
+		if (algo_flags&PARAM_STR)
+			LM_ERR("unable to get algorithm from [%.*s]\n", s_algo.len, s_algo.s);
+		else
+			LM_ERR("unable to get algorithm\n");
 		return -1;
 	}
 
@@ -974,6 +990,7 @@ static void dispatcher_rpc_list(rpc_t* rpc, void* ctx)
 	void* rh;
 	void* sh;
 	void* vh;
+	void* wh;
 	int j;
 	char c[3];
 	str data = {"", 0};
@@ -1005,7 +1022,6 @@ static void dispatcher_rpc_list(rpc_t* rpc, void* ctx)
 		return;
 	}
 
-
 	for(list = ds_list; list!= NULL; list= list->next)
 	{
 		if (rpc->struct_add(ih, "{", "SET", &sh) < 0)
@@ -1045,15 +1061,36 @@ static void dispatcher_rpc_list(rpc_t* rpc, void* ctx)
 			else
 				c[1] = 'X';
 
-			if(rpc->struct_add(vh, "SsdS",
+			if (list->dlist[j].attrs.body.s)
+			{
+				if(rpc->struct_add(vh, "Ssd{",
 						"URI", &list->dlist[j].uri,
 						"FLAGS", c,
 						"PRIORITY", list->dlist[j].priority,
-						"ATTRS", (list->dlist[j].attrs.body.s)?
-						&(list->dlist[j].attrs.body):&data)<0)
-			{
-				rpc->fault(ctx, 500, "Internal error creating dest struct");
-				return;
+						"ATTRS", &wh)<0)
+				{
+					rpc->fault(ctx, 500, "Internal error creating dest struct");
+					return;
+				}
+				if(rpc->struct_add(wh, "SSdd",
+						"BODY", &(list->dlist[j].attrs.body),
+						"DUID", (list->dlist[j].attrs.duid.s)?
+						&(list->dlist[j].attrs.duid):&data,
+						"MAXLOAD", list->dlist[j].attrs.maxload,
+						"WEIGHT", list->dlist[j].attrs.weight)<0)
+				{
+					rpc->fault(ctx, 500, "Internal error creating attrs struct");
+					return;
+				}
+			} else {
+				if(rpc->struct_add(vh, "Ssd",
+						"URI", &list->dlist[j].uri,
+						"FLAGS", c,
+						"PRIORITY", list->dlist[j].priority)<0)
+				{
+					rpc->fault(ctx, 500, "Internal error creating dest struct");
+					return;
+				}
 			}
 		}
 	}

+ 1 - 0
modules/ims_auth/api.h

@@ -53,6 +53,7 @@
  * return codes to config by auth functions
  */
 typedef enum auth_cfg_result {
+	AUTH_RESYNC_REQUESTED = -9,	/*!< resync requested from UE via auts param */
 	AUTH_USER_MISMATCH = -8,    /*!< Auth user != From/To user */
 	AUTH_NONCE_REUSED = -6,     /*!< Returned if nonce is used more than once */
 	AUTH_NO_CREDENTIALS = -5,   /*!< Credentials missing */

+ 1 - 0
modules/ims_auth/authims_mod.c

@@ -119,6 +119,7 @@ int ignore_failed_auth = 0;
 static cmd_export_t cmds[] = {
     {"ims_www_authenticate", (cmd_function) www_authenticate, 1, auth_fixup, 0, REQUEST_ROUTE},
     {"ims_www_challenge", (cmd_function) www_challenge, 2, challenge_fixup_async, 0, REQUEST_ROUTE},
+    {"ims_www_resync_auth", (cmd_function) www_resync_auth, 2, challenge_fixup_async, 0, REQUEST_ROUTE},
     {"ims_proxy_authenticate", (cmd_function) proxy_authenticate, 1, auth_fixup, 0, REQUEST_ROUTE},
     {"ims_proxy_challenge", (cmd_function) proxy_challenge, 2, auth_fixup_async, 0, REQUEST_ROUTE},
     {"bind_ims_auth", (cmd_function) bind_ims_auth, 0, 0, 0, 0},

+ 187 - 27
modules/ims_auth/authorize.c

@@ -276,7 +276,6 @@ int challenge(struct sip_msg* msg, char* str1, char* str2, int is_proxy_auth, ch
     str private_identity, public_identity, auts = {0, 0}, nonce = {0, 0};
     auth_vector *av = 0;
     int algo_type;
-    
     str route_name;
 
     saved_transaction_t* saved_t;
@@ -346,8 +345,8 @@ int challenge(struct sip_msg* msg, char* str1, char* str2, int is_proxy_auth, ch
 
     algo_type = registration_default_algorithm_type;
 
-    /* check if it is a synchronization request */
-    //TODO this is MAR syncing - have removed it currently - TOD maybe put back in
+//    /* check if it is a synchronization request */
+//    //TODO this is MAR syncing - have removed it currently - TOD maybe put back in
 //    auts = ims_get_auts(msg, realm, is_proxy_auth);
 //    if (auts.len) {
 //        LM_DBG("IMS Auth Synchronization requested <%.*s>\n", auts.len, auts.s);
@@ -363,19 +362,15 @@ int challenge(struct sip_msg* msg, char* str1, char* str2, int is_proxy_auth, ch
 //            av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_SENT, &nonce, &aud_hash);
 //
 //        if (!av) {
-//            LM_ERR("Nonce not regonized as sent, no sync!\n");
+//            LM_ERR("nonce not recognized as sent, no sync!\n");
 //            auts.len = 0;
 //            auts.s = 0;
 //        } else {
 //            av->status = AUTH_VECTOR_USELESS;
 //            auth_data_unlock(aud_hash);
 //            av = 0;
+//            resync = 1;
 //        }
-//
-//        //RICHARD REMOVED REALM - this is diameter realm set in cxdx not SIP domain
-//        // if synchronization - force MAR - if MAR ok, old avs will be droped
-//        multimedia_auth_request(msg, public_identity, private_identity, av_request_at_sync,
-//                auth_scheme_types[algo_type], nonce, auts, scscf_name_str);
 //    }
 
     //RICHARD changed this
@@ -446,7 +441,6 @@ int challenge(struct sip_msg* msg, char* str1, char* str2, int is_proxy_auth, ch
         memcpy(saved_t->realm.s, realm.s, realm.len);
         saved_t->realm.len = realm.len;
 
-
         saved_t->is_proxy_auth = is_proxy_auth;
 
         LM_DBG("Suspending SIP TM transaction\n");
@@ -474,6 +468,158 @@ int www_challenge(struct sip_msg* msg, char* _route, char* str1, char* str2) {
     return challenge(msg, str1, str2, 0, _route);
 }
 
+int www_resync_auth(struct sip_msg* msg, char* _route, char* str1, char* str2) {
+
+    str realm = {0, 0};
+    unsigned int aud_hash;
+    str private_identity, public_identity, auts = {0, 0}, nonce = {0, 0};
+    auth_vector *av = 0;
+    int algo_type;
+    int is_proxy_auth=0;
+    str route_name;
+
+    saved_transaction_t* saved_t;
+    tm_cell_t *t = 0;
+    cfg_action_t* cfg_action;
+
+    if (fixup_get_svalue(msg, (gparam_t*) _route, &route_name) != 0) {
+        LM_ERR("no async route block for assign_server_unreg\n");
+        return -1;
+    }
+
+    LM_DBG("Looking for route block [%.*s]\n", route_name.len, route_name.s);
+    int ri = route_get(&main_rt, route_name.s);
+    if (ri < 0) {
+        LM_ERR("unable to find route block [%.*s]\n", route_name.len, route_name.s);
+        return -1;
+    }
+    cfg_action = main_rt.rlist[ri];
+    if (cfg_action == NULL) {
+        LM_ERR("empty action lists in route block [%.*s]\n", route_name.len, route_name.s);
+        return -1;
+    }
+
+    if (get_str_fparam(&realm, msg, (fparam_t*) str1) < 0) {
+        LM_ERR("failed to get realm value\n");
+        return CSCF_RETURN_ERROR;
+    }
+
+    if (realm.len == 0) {
+        LM_ERR("invalid realm value - empty content\n");
+        return CSCF_RETURN_ERROR;
+    }
+
+    create_return_code(CSCF_RETURN_ERROR);
+
+    if (msg->first_line.type != SIP_REQUEST) {
+        LM_ERR("This message is not a request\n");
+        return CSCF_RETURN_ERROR;
+    }
+
+    /* get the private_identity */
+    private_identity = get_private_identity(msg, realm, is_proxy_auth);
+    if (!private_identity.len) {
+        LM_ERR("No private identity specified (Authorization: username)\n");
+        stateful_request_reply(msg, 403, MSG_403_NO_PRIVATE);
+        return CSCF_RETURN_BREAK;
+    }
+    /* get the public_identity */
+    public_identity = get_public_identity(msg);
+    if (!public_identity.len) {
+        LM_ERR("No public identity specified (To:)\n");
+        stateful_request_reply(msg, 403, MSG_403_NO_PUBLIC);
+        return CSCF_RETURN_BREAK;
+    }
+
+    algo_type = registration_default_algorithm_type;
+
+    /* check if it is a synchronization request */
+    //TODO this is MAR syncing - have removed it currently - TOD maybe put back in
+    auts = ims_get_auts(msg, realm, is_proxy_auth);
+    if (auts.len) {
+        LM_DBG("IMS Auth Synchronization requested <%.*s>\n", auts.len, auts.s);
+
+        nonce = ims_get_nonce(msg, realm);
+        if (nonce.len == 0) {
+            LM_DBG("Nonce not found (Authorization: nonce)\n");
+            stateful_request_reply(msg, 403, MSG_403_NO_NONCE);
+            return CSCF_RETURN_BREAK;
+        }
+        av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_USED, &nonce, &aud_hash);
+        if (!av)
+            av = get_auth_vector(private_identity, public_identity, AUTH_VECTOR_SENT, &nonce, &aud_hash);
+
+        if (!av) {
+            LM_ERR("nonce not recognized as sent, no sync!\n");
+            auts.len = 0;
+            auts.s = 0;
+        } else {
+            av->status = AUTH_VECTOR_USELESS;
+            auth_data_unlock(aud_hash);
+            av = 0;
+        }
+    }
+
+	//before we send lets suspend the transaction
+	t = tmb.t_gett();
+	if (t == NULL || t == T_UNDEFINED) {
+		if (tmb.t_newtran(msg) < 0) {
+			LM_ERR("cannot create the transaction for MAR async\n");
+			stateful_request_reply(msg, 480, MSG_480_DIAMETER_ERROR);
+			return CSCF_RETURN_BREAK;
+		}
+		t = tmb.t_gett();
+		if (t == NULL || t == T_UNDEFINED) {
+			LM_ERR("cannot lookup the transaction\n");
+			stateful_request_reply(msg, 480, MSG_480_DIAMETER_ERROR);
+			return CSCF_RETURN_BREAK;
+		}
+	}
+
+	saved_t = shm_malloc(sizeof(saved_transaction_t));
+	if (!saved_t) {
+		LM_ERR("no more memory trying to save transaction state\n");
+		return CSCF_RETURN_ERROR;
+
+	}
+	memset(saved_t, 0, sizeof(saved_transaction_t));
+	saved_t->act = cfg_action;
+
+	saved_t->realm.s = (char*) shm_malloc(realm.len + 1);
+	if (!saved_t->realm.s) {
+		LM_ERR("no more memory trying to save transaction state : callid\n");
+		shm_free(saved_t);
+		return CSCF_RETURN_ERROR;
+	}
+	memset(saved_t->realm.s, 0, realm.len + 1);
+	memcpy(saved_t->realm.s, realm.s, realm.len);
+	saved_t->realm.len = realm.len;
+
+	saved_t->is_proxy_auth = is_proxy_auth;
+	saved_t->is_resync = 1;
+
+	LM_DBG("Suspending SIP TM transaction\n");
+	if (tmb.t_suspend(msg, &saved_t->tindex, &saved_t->tlabel) < 0) {
+		LM_ERR("failed to suspend the TM processing\n");
+		free_saved_transaction_data(saved_t);
+
+		stateful_request_reply(msg, 480, MSG_480_DIAMETER_ERROR);
+		return CSCF_RETURN_BREAK;
+	}
+
+	if (multimedia_auth_request(msg, public_identity, private_identity,
+			av_request_at_sync, auth_scheme_types[algo_type], nonce, auts,
+			scscf_name_str, saved_t) != 0) {
+		LM_ERR("ERR:I_MAR: Error sending MAR or MAR time-out\n");
+		tmb.t_cancel_suspend(saved_t->tindex, saved_t->tlabel);
+		free_saved_transaction_data(saved_t);
+		stateful_request_reply(msg, 480, MSG_480_DIAMETER_ERROR);
+		return CSCF_RETURN_BREAK;
+	}
+
+	return CSCF_RETURN_BREAK;
+}
+
 int proxy_challenge(struct sip_msg* msg, char* _route, char* str1, char* str2) {
     return challenge(msg, str1, str2, 1, _route);
 }
@@ -554,7 +700,7 @@ int authenticate(struct sip_msg* msg, char* _realm, char* str2, int is_proxy_aut
     unsigned int aud_hash = 0;
     str realm;
     str private_identity, public_identity;
-    str nonce, response16, nc, cnonce, qop_str = {0, 0}, body, *next_nonce = &empty_s;
+    str nonce, response16, nc, cnonce, qop_str = {0, 0}, auts = {0, 0}, body, *next_nonce = &empty_s;
     enum qop_type qop = QOP_UNSPEC;
     str uri = {0, 0};
     HASHHEX expected, ha1, hbody, rspauth;
@@ -631,7 +777,7 @@ int authenticate(struct sip_msg* msg, char* _realm, char* str2, int is_proxy_aut
         /* if none found, or nonce reuse is disabled, look for a fresh vector
          * We should also drop every other used vector at this point
          * (there souldn't be more than one) */
-
+    	LM_DBG("Looking for auth vector based on IMPI: [%.*s] and IMPU: [%.*s]\n", private_identity.len, private_identity.s, public_identity.len, public_identity.s);
         auth_userdata *aud;
         auth_vector *av_it;
         aud = get_auth_userdata(private_identity, public_identity);
@@ -815,7 +961,15 @@ int authenticate(struct sip_msg* msg, char* _realm, char* str2, int is_proxy_aut
                 authenticate_hex_len,authenticate_hex,
                 authorise_len,
                 authorise_len, authorise);
-        ret = AUTH_INVALID_PASSWORD;
+//        /* check for auts in authorization header - if it is then we need to resync */
+		auts = ims_get_auts(msg, realm, is_proxy_auth);
+		if (auts.len) {
+			LM_DBG("IMS Auth Synchronization requested <%.*s>\n", auts.len, auts.s);
+			ret = AUTH_RESYNC_REQUESTED;
+			av->status = AUTH_VECTOR_SENT;
+		} else {
+			ret = AUTH_INVALID_PASSWORD;
+		}
     }
 
     if (ignore_failed_auth) {
@@ -1113,6 +1267,8 @@ auth_vector * new_auth_vector(int item_number, str auth_scheme, str authenticate
     x->status = AUTH_VECTOR_UNUSED;
     x->expires = 0;
 
+    LM_DBG("new auth-vector with ck [%.*s] with status %d\n", x->ck.len, x->ck.s, x->status);
+
 done:
     return x;
 }
@@ -1315,26 +1471,28 @@ int multimedia_auth_request(struct sip_msg *msg, str public_identity, str privat
     str authorization = {0, 0};
     int result = -1;
 
-    //TODO this is MAR syncing - have removed it currently - TOD maybe put back in
-    //int is_sync = 0;
-//    if (auts.len) {
-//        authorization.s = pkg_malloc(nonce.len * 3 / 4 + auts.len * 3 / 4 + 8);
-//        if (!authorization.s) goto done;
-//        authorization.len = base64_to_bin(nonce.s, nonce.len, authorization.s);
-//        authorization.len = RAND_LEN;
-//        authorization.len += base64_to_bin(auts.s, auts.len, authorization.s + authorization.len);
-//        is_sync = 1;
-//    }
+    int is_sync = 0;
+    if (auts.len) {
+        authorization.s = pkg_malloc(nonce.len * 3 / 4 + auts.len * 3 / 4 + 8);
+        if (!authorization.s)  {
+        	LM_ERR("no more pkg mem\n");
+        	return result;
+        }
+        authorization.len = base64_to_bin(nonce.s, nonce.len, authorization.s);
+        authorization.len = RAND_LEN;
+        authorization.len += base64_to_bin(auts.s, auts.len, authorization.s + authorization.len);
+        is_sync = 1;
+    }
+
+    if (is_sync) {
+    	drop_auth_userdata(private_identity, public_identity);
+    }
 
 
     LM_DBG("Sending MAR\n");
     result = cxdx_send_mar(msg, public_identity, private_identity, count, auth_scheme, authorization, servername, transaction_data);
     if (authorization.s) pkg_free(authorization.s);
 
-    //TODO this is MAR syncing - have removed it currently - TOD maybe put back in
-    //if (is_sync)
-    //    drop_auth_userdata(private_identity, public_identity);
-
     return result;
 }
 
@@ -1500,12 +1658,14 @@ int drop_auth_userdata(str private_identity, str public_identity) {
 
     av = aud->head;
     while (av) {
+    	LM_DBG("dropping auth vector that was in status %d\n", av->status);
         av->status = AUTH_VECTOR_USELESS;
         av = av->next;
     }
     auth_data_unlock(aud->hash);
     return 1;
 error:
+	LM_DBG("no authdata to drop any auth vectors\n");
     if (aud) auth_data_unlock(aud->hash);
     return 0;
 }

+ 1 - 0
modules/ims_auth/authorize.h

@@ -140,6 +140,7 @@ int proxy_challenge(struct sip_msg* msg, char* route, char* _realm, char* str2);
  */
 int www_authenticate(struct sip_msg* _msg, char* _realm, char* _table);
 int www_challenge(struct sip_msg* msg, char* route, char* _realm, char* str2);
+int www_resync_auth(struct sip_msg* msg, char* _route, char* str1, char* str2);
 
 
 /*

+ 21 - 18
modules/ims_auth/cxdx_mar.c

@@ -387,8 +387,6 @@ success:
         //TODO need to confirm that removing this has done no problems
         //tmp->auth_data->code = -tmp->auth_data->code;
 
-	LM_DBG("Added new auth-vector.\n");
-
         tmp = tmp->next;
     }
 
@@ -396,29 +394,34 @@ success:
     //right now we take the first and put the rest in the AV queue
     //then we use the first one and then add it to the queue as sent!
 
-    for (i = 1; i < sip_number_auth_items; i++)
+    for (i = 1; i < sip_number_auth_items; i++) {
         if (!add_auth_vector(private_identity, public_identity, avlist[i]))
             free_auth_vector(avlist[i]);
-
-    if (!pack_challenge(t->uas.request, data->realm, avlist[0], data->is_proxy_auth)) {
-        stateful_request_reply_async(t, t->uas.request, 500, MSG_500_PACK_AV);
-        result = CSCF_RETURN_FALSE;
-        goto done;
     }
 
-    if (data->is_proxy_auth)
-        stateful_request_reply_async(t, t->uas.request, 407, MSG_407_CHALLENGE);
-    else
-        stateful_request_reply_async(t, t->uas.request, 401, MSG_401_CHALLENGE);
+    if (!data->is_resync) {
+		if (!pack_challenge(t->uas.request, data->realm, avlist[0], data->is_proxy_auth)) {
+			stateful_request_reply_async(t, t->uas.request, 500, MSG_500_PACK_AV);
+			result = CSCF_RETURN_FALSE;
+			goto done;
+		}
+
+		if (data->is_proxy_auth)
+			stateful_request_reply_async(t, t->uas.request, 407, MSG_407_CHALLENGE);
+		else
+			stateful_request_reply_async(t, t->uas.request, 401, MSG_401_CHALLENGE);
+    }
 
 done:
-    if (avlist) {
-        start_reg_await_timer(avlist[0]); //start the timer to remove stale or unused Auth Vectors
 
-        //now we add it to the queue as sent as we have already sent the challenge and used it and set the status to SENT
-        if (!add_auth_vector(private_identity, public_identity, avlist[0]))
-            free_auth_vector(avlist[0]);
-    }
+	if (avlist) {
+		if (!data->is_resync)	//only start the timer if we used the vector above - we dont use it resync mode
+		start_reg_await_timer(avlist[0]); //start the timer to remove stale or unused Auth Vectors
+
+		//now we add it to the queue as sent as we have already sent the challenge and used it and set the status to SENT
+		if (!add_auth_vector(private_identity, public_identity, avlist[0]))
+			free_auth_vector(avlist[0]);
+	}
 
     //free memory
     if (maa) cdpb.AAAFreeMessage(&maa);

+ 1 - 0
modules/ims_auth/cxdx_mar.h

@@ -63,6 +63,7 @@ typedef struct saved_transaction {
 	unsigned int ticks;
 	cfg_action_t *act;
 	int is_proxy_auth;
+	int is_resync;
     str realm;
 } saved_transaction_t;
 

+ 3 - 2
modules/ims_charging/ccr.c

@@ -2,8 +2,10 @@
 
 #include "ccr.h"
 #include "Ro_data.h"
+#include "ro_avp.h"
 
 extern cdp_avp_bind_t *cdp_avp;
+extern struct cdp_binds cdpb;
 
 int Ro_write_event_type_avps(AAA_AVP_LIST * avp_list, event_type_t * x) {
     AAA_AVP_LIST aList = {0, 0};
@@ -187,9 +189,8 @@ AAAMessage * Ro_write_CCR_avps(AAAMessage * ccr, Ro_CCR_t* x) {
     if (!ccr) return 0;
 
     if (!cdp_avp->base.add_Origin_Host(&(ccr->avpList), x->origin_host, 0)) goto error;
-
     if (!cdp_avp->base.add_Origin_Realm(&(ccr->avpList), x->origin_realm, 0)) goto error;
-    if (!cdp_avp->base.add_Destination_Realm(&(ccr->avpList), x->destination_realm, 0)) goto error;
+    if (!ro_add_destination_realm_avp(ccr, x->destination_realm)) goto error;
 
     if (!cdp_avp->base.add_Accounting_Record_Type(&(ccr->avpList), x->acct_record_type)) goto error;
     if (!cdp_avp->base.add_Accounting_Record_Number(&(ccr->avpList), x->acct_record_number)) goto error;

+ 0 - 1
modules/ims_charging/config.h

@@ -5,7 +5,6 @@ typedef struct {
     str origin_host;
     str origin_realm;
     str destination_realm;
-    str destination_host;
     str * service_context_id;
 } client_ro_cfg;
 

+ 24 - 49
modules/ims_charging/ims_ro.c

@@ -29,12 +29,14 @@
 #include "config.h"
 #include "ro_session_hash.h"
 #include "stats.h"
+#include "ro_avp.h"
 
 extern struct tm_binds tmb;
 extern struct cdp_binds cdpb;
 extern client_ro_cfg cfg;
 extern struct dlg_binds dlgb;
 extern cdp_avp_bind_t *cdp_avp;
+extern str ro_forced_peer;
 
 struct session_setup_data {
 	struct ro_session *ro_session;
@@ -84,34 +86,6 @@ struct sip_msg * trans_get_request_from_current_reply() {
     else return 0;
 }
 
-/**
- * Create and add an AVP to a Diameter message.
- * @param m - Diameter message to add to
- * @param d - the payload data
- * @param len - length of the payload data
- * @param avp_code - the code of the AVP
- * @param flags - flags for the AVP
- * @param vendorid - the value of the vendor id or 0 if none
- * @param data_do - what to do with the data when done
- * @param func - the name of the calling function, for debugging purposes
- * @returns 1 on success or 0 on failure
- */
-static inline int Ro_add_avp(AAAMessage *m, char *d, int len, int avp_code, int flags, int vendorid, int data_do, const char *func) {
-    AAA_AVP *avp;
-    if (vendorid != 0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
-    avp = cdpb.AAACreateAVP(avp_code, flags, vendorid, d, len, data_do);
-    if (!avp) {
-        LM_ERR("%s: Failed creating avp\n", func);
-        return 0;
-    }
-    if (cdpb.AAAAddAVPToMessage(m, avp, m->avpList.tail) != AAA_ERR_SUCCESS) {
-        LM_ERR("%s: Failed adding avp to message\n", func);
-       cdpb.AAAFreeAVP(&avp);
-        return 0;
-    }
-    return 1;
-}
-
 /**
  * Create and add an AVP to a list of AVPs.
  * @param list - the AVP list to add to
@@ -148,22 +122,6 @@ static inline int Ro_add_avp_list(AAA_AVP_LIST *list, char *d, int len, int avp_
     return 1;
 }
 
-/**
- * Creates and adds a Destination-Realm AVP.
- * @param msg - the Diameter message to add to.
- * @param data - the value for the AVP payload
- * @returns 1 on success or 0 on error
- */
-inline int ro_add_destination_realm_avp(AAAMessage *msg, str data) {
-    return
-    Ro_add_avp(msg, data.s, data.len,
-            AVP_Destination_Realm,
-            AAA_AVP_FLAG_MANDATORY,
-            0,
-            AVP_DUPLICATE_DATA,
-            __FUNCTION__);
-}
-
 inline int Ro_add_cc_request(AAAMessage *msg, unsigned int cc_request_type, unsigned int cc_request_number) {
     char x[4];
     set_4bytes(x, cc_request_type);
@@ -631,8 +589,11 @@ void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned
 
     cdpb.AAASessionsUnlock(auth->hash);
 
-    //AAAMessage *cca = cdpb.AAASendRecvMessageToPeer(ccr, &cfg.destination_host);
-    cdpb.AAASendMessageToPeer(ccr, &cfg.destination_host, resume_on_interim_ccr, (void *) i_req);
+    if (ro_forced_peer.len > 0) {
+    	cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_interim_ccr, (void *) i_req);
+    } else {
+    	cdpb.AAASendMessage(ccr, resume_on_interim_ccr, (void *) i_req);
+    }
 
 //    cdpb.AAASessionsUnlock(auth->hash);
 
@@ -850,7 +811,12 @@ void send_ccr_stop(struct ro_session *ro_session) {
     }
 
     cdpb.AAASessionsUnlock(auth->hash);
-    cdpb.AAASendMessageToPeer(ccr, &cfg.destination_host, resume_on_termination_ccr, NULL);
+
+    if (ro_forced_peer.len > 0) {
+    	cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_termination_ccr, NULL);
+    } else {
+    	cdpb.AAASendMessage(ccr, resume_on_termination_ccr, NULL);
+    }
 
     Ro_free_CCR(ro_ccr_data);
 
@@ -1050,7 +1016,14 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, str* charge_
 
     LM_DBG("Sending CCR Diameter message.\n");
     cdpb.AAASessionsUnlock(cc_acc_session->hash);
-    cdpb.AAASendMessageToPeer(ccr, &cfg.destination_host, resume_on_initial_ccr, (void *) ssd);
+
+    if (ro_forced_peer.len > 0) {
+    	LM_DBG("Sending message with Peer\n");
+    	cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_initial_ccr, (void *) ssd);
+    } else {
+    	LM_DBG("Sending message without Peer and realm is [%.*s]\n", ccr->dest_realm->data.len, ccr->dest_realm->data.s);
+    	cdpb.AAASendMessage(ccr, resume_on_initial_ccr, (void *) ssd);
+    }
 
     Ro_free_CCR(ro_ccr_data);
 
@@ -1177,7 +1150,9 @@ error0:
     LM_DBG("Trying to reserve credit on initial INVITE failed on cdp callback\n");
     create_cca_return_code(error_code);
 
-    cdpb.AAAFreeMessage(&cca);
+    if (!is_timeout && cca) {
+    	cdpb.AAAFreeMessage(&cca);
+    }
 
     if (t)
     	tmb.unref_cell(t);

+ 1 - 4
modules/ims_charging/mod.c

@@ -122,7 +122,7 @@ stat_export_t charging_stats[] = {
     {"ccr_responses_time", STAT_NO_RESET, &ccr_responses_time},
     {"billed_secs", STAT_NO_RESET, &billed_secs},
     {"killed_calls", STAT_NO_RESET, &killed_calls},
-    {"ccr_timeouts", STAT_NO_RESET, &ccr_timeouts},
+    {"ccr_timeouts", 0, &ccr_timeouts},
     {0, 0, 0}
 };
 
@@ -150,9 +150,6 @@ int fix_parameters() {
 	cfg.destination_realm.s = ro_destination_realm_s;
 	cfg.destination_realm.len = strlen(ro_destination_realm_s);
 
-	cfg.destination_host.s = ro_destination_host_s;
-	cfg.destination_host.len = strlen(ro_destination_host_s);
-
 	cfg.service_context_id = shm_malloc(sizeof(str));
 	if (!cfg.service_context_id) {
 		LM_ERR("fix_parameters:not enough shm memory\n");

+ 53 - 0
modules/ims_charging/ro_avp.c

@@ -0,0 +1,53 @@
+/*
+ * ro_avp.c
+ *
+ *  Created on: 29 May 2014
+ *      Author: jaybeepee
+ */
+#include "ro_avp.h"
+
+extern struct cdp_binds cdpb;
+/**
+ * Creates and adds a Destination-Realm AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+int ro_add_destination_realm_avp(AAAMessage *msg, str data) {
+    return
+    Ro_add_avp(msg, data.s, data.len,
+            AVP_Destination_Realm,
+            AAA_AVP_FLAG_MANDATORY,
+            0,
+            AVP_DUPLICATE_DATA,
+            __FUNCTION__);
+}
+
+/**
+ * Create and add an AVP to a Diameter message.
+ * @param m - Diameter message to add to
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+int Ro_add_avp(AAAMessage *m, char *d, int len, int avp_code, int flags, int vendorid, int data_do, const char *func) {
+    AAA_AVP *avp;
+    if (vendorid != 0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+    avp = cdpb.AAACreateAVP(avp_code, flags, vendorid, d, len, data_do);
+    if (!avp) {
+        LM_ERR("%s: Failed creating avp\n", func);
+        return 0;
+    }
+    if (cdpb.AAAAddAVPToMessage(m, avp, m->avpList.tail) != AAA_ERR_SUCCESS) {
+        LM_ERR("%s: Failed adding avp to message\n", func);
+       cdpb.AAAFreeAVP(&avp);
+        return 0;
+    }
+    return 1;
+}
+

+ 17 - 0
modules/ims_charging/ro_avp.h

@@ -0,0 +1,17 @@
+/*
+ * ro_avp.h
+ *
+ *  Created on: 29 May 2014
+ *      Author: jaybeepee
+ */
+
+#ifndef RO_AVP_H_
+#define RO_AVP_H_
+
+#include "../cdp/cdp_load.h"
+
+int ro_add_destination_realm_avp(AAAMessage *msg, str data);
+int Ro_add_avp(AAAMessage *m, char *d, int len, int avp_code, int flags, int vendorid, int data_do, const char *func);
+
+
+#endif /* RO_AVP_H_ */

+ 4 - 0
modules/ims_registrar_pcscf/notify.c

@@ -68,6 +68,7 @@
 #define EVENT_CREATED 3
 #define EVENT_REFRESHED 4
 #define EVENT_EXPIRED 5
+#define EVENT_DEACTIVATED 6
 
 #define RESULT_ERROR -1
 #define RESULT_CONTACTS_FOUND 1
@@ -182,6 +183,9 @@ int reginfo_parse_event(char * s) {
 			if (strncmp(s, "registered", 10) ==  0) return EVENT_REGISTERED;
 			if (strncmp(s, "terminated", 10) ==  0) return EVENT_TERMINATED;
 			break;
+	    case 11:
+			if (strncmp(s, "deactivated", 11) ==  0) return EVENT_DEACTIVATED;
+			break;
 		case 12:
 			if (strncmp(s, "unregistered", 12) ==  0) return EVENT_UNREGISTERED;
 			break;

+ 2 - 1
modules/ims_registrar_scscf/registrar_notify.c

@@ -1444,6 +1444,7 @@ str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus) {
         res = ul.get_impurecord(_t, &(impu_list[i]), &r);
         if (res != 0) {
             LM_WARN("impu disappeared, ignoring it\n");
+	    ul.unlock_udomain(_t, &impu_list[i]);
             continue;
         }
         LM_DBG("Retrieved IMPU record");
@@ -1600,7 +1601,7 @@ str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type) {
             }
             if (c->q != -1) {
                 float q = (float) c->q / 1000;
-                sprintf(pad.s, contact_s_q.s, c, r_active.len, r_active.s, r_registered.len, r_registered.s, c->expires - act_time, q);
+		sprintf(pad.s, contact_s_q.s, c, state.len, state.s, event.len, event.s, expires, q);
             } else
                 sprintf(pad.s, contact_s.s, c, state.len, state.s, event.len, event.s, expires);
             pad.len = strlen(pad.s);

+ 3 - 4
modules/ims_usrloc_pcscf/udomain.c

@@ -715,18 +715,17 @@ static inline pcontact_info_t* dbrow2info( db_val_t *vals, str *contact)
 	if (VAL_NULL(vals+5) || !rx_session_id.s || !rx_session_id.s[0]) {
 		rx_session_id.len = 0;
 		rx_session_id.s = 0;
-		LM_DBG("2\n");
 	} else {
 		rx_session_id.len = strlen(rx_session_id.s);
 	}
 	ci.rx_regsession_id = &rx_session_id;
 	if (VAL_NULL(vals + 6)) {
-		LM_CRIT("empty registration state in DB\n");
+		LM_ERR("empty registration state in DB\n");
 		return 0;
 	}
 	ci.reg_state = VAL_INT(vals + 6);
 	if (VAL_NULL(vals + 7)) {
-		LM_CRIT("empty expire\n");
+		LM_ERR("empty expire\n");
 		return 0;
 	}
 	ci.expires = VAL_TIME(vals + 7);
@@ -741,7 +740,7 @@ static inline pcontact_info_t* dbrow2info( db_val_t *vals, str *contact)
 
 	//public IDs - implicit set
 	implicit_impus.s = (char*) VAL_STRING(vals + 10);
-	if (!VAL_NULL(vals + 8) && implicit_impus.s && implicit_impus.s[0]) {
+	if (!VAL_NULL(vals + 10) && implicit_impus.s && implicit_impus.s[0]) {
 		//how many
 		n=0;
 		p = implicit_impus.s;

+ 6 - 0
modules/ims_usrloc_scscf/usrloc_db.c

@@ -637,6 +637,7 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) {
 			if (impu_id < 0) {
 				LM_ERR("impu_id has not been set [%.*s] - we cannot read contacts from DB....aborting preload\n", impu.len, impu.s);
 				//TODO: check frees
+				unlock_udomain(_d, &impu);
 				continue;
 			}
 			impu_id_len = int_to_str_len(impu_id);
@@ -649,6 +650,7 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) {
 				if (!query_buffer.s) {
 					LM_ERR("mo more pkg mem\n");
 					//TODO: check free
+					unlock_udomain(_d, &impu);
 					return -1;
 				}
 				query_buffer_len = len;
@@ -661,11 +663,13 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) {
 				LM_ERR("Unable to query DB for contacts associated with impu [%.*s]\n",
 						impu.len, impu.s);
 				ul_dbf.free_result(_c, contact_rs);
+				unlock_udomain(_d, &impu);
 				continue;
 			}
 			if (RES_ROW_N(contact_rs) == 0) {
 				LM_DBG("no contacts associated with impu [%.*s]\n",impu.len, impu.s);
 				ul_dbf.free_result(_c, contact_rs);
+				unlock_udomain(_d, &impu);
 				continue;
 			}
 
@@ -682,6 +686,7 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) {
 					}
 					if (dbrow2contact(contact_vals, &contact_data) != 0) {
 						LM_ERR("unable to convert contact row from DB into valid data... moving on\n");
+						unlock_udomain(_d, &impu);
 						continue;
 					}
 
@@ -695,6 +700,7 @@ int preload_udomain(db1_con_t* _c, udomain_t* _d) {
 					if (ul_dbf.fetch_result(_c, &contact_rs, ul_fetch_rows) < 0) {
 						LM_ERR("fetching rows failed\n");
 						ul_dbf.free_result(_c, contact_rs);
+						unlock_udomain(_d, &impu);
 						return -1;
 					}
 				} else {

+ 1 - 0
modules/ipops/ipops_pv.c

@@ -262,6 +262,7 @@ int pv_parse_dns_name(pv_spec_t *sp, str *in)
 
 error:
 	LM_ERR("error at PV dns name: %.*s\n", in->len, in->s);
+	if(dpv) pkg_free(dpv);
 	return -1;
 }
 

+ 3 - 1
modules/kex/core_stats.c

@@ -122,6 +122,8 @@ static int km_cb_req_stats(struct sip_msg *msg,
 		unsigned int flags, void *param)
 {
 	update_stat(rcv_reqs, 1);
+	if(!IS_SIP(msg))
+		return 1;
 	if(msg->first_line.u.request.method_value==METHOD_OTHER)
 		update_stat(unsupported_methods, 1);
 	return 1;
@@ -462,7 +464,7 @@ static const char* rpc_stats_clear_statistics_doc[2] =
 rpc_export_t kex_stats_rpc[] =
 {
 	{"stats.get_statistics",   rpc_stats_get_statistics,
-							rpc_stats_get_statistics_doc,   0},
+							rpc_stats_get_statistics_doc,   RET_ARRAY},
 	{"stats.reset_statistics", rpc_stats_reset_statistics,
 							rpc_stats_reset_statistics_doc, 0},
 	{"stats.clear_statistics", rpc_stats_clear_statistics,

+ 4 - 4
modules/mtree/mtree_mod.c

@@ -555,8 +555,7 @@ static int mt_load_db(m_tree_t *pt)
 		} else {
 			if(RES_ROW_N(db_res)==0)
 			{
-				mt_dbf.free_result(db_con, db_res);
-				return 0;
+				goto dbreloaded;
 			}
 		}
 	} else {
@@ -566,8 +565,7 @@ static int mt_load_db(m_tree_t *pt)
 		{
 			if(ret==0)
 			{
-				mt_dbf.free_result(db_con, db_res);
-				return 0;
+				goto dbreloaded;
 			} else {
 				goto error;
 			}
@@ -622,6 +620,8 @@ static int mt_load_db(m_tree_t *pt)
 			break;
 		}
 	}  while(RES_ROW_N(db_res)>0);
+
+dbreloaded:
 	mt_dbf.free_result(db_con, db_res);
 
 

+ 4 - 2
modules/presence_dialoginfo/notify_body.c

@@ -101,7 +101,7 @@ str* agregate_xmls(str* pres_user, str* pres_domain, str** body_array, int n)
 	xmlDocPtr  doc = NULL;
 	xmlNodePtr root_node = NULL;
 	xmlNsPtr   namespace = NULL;
-	int winner_priority = -1, priority;
+	/*int winner_priority = -1, priority;*/
 
 	xmlNodePtr p_root= NULL;
 	xmlDocPtr* xml_array ;
@@ -111,6 +111,7 @@ str* agregate_xmls(str* pres_user, str* pres_domain, str** body_array, int n)
 	xmlNodePtr confirmed_node = NULL;
 	xmlNodePtr proceed_node = NULL;
 	xmlNodePtr trying_node = NULL;
+	xmlNodePtr next_node = NULL;
 
 
 	char *state = NULL;
@@ -214,7 +215,8 @@ str* agregate_xmls(str* pres_user, str* pres_domain, str** body_array, int n)
 			goto error;
 		}
 		if (p_root->children) {
-			for (node = p_root->children; node; node = node->next) {
+			for (node = p_root->children; node; node = next_node) {
+				next_node = node->next;
 				if (node->type == XML_ELEMENT_NODE) {
 					LM_DBG("node type: Element, name: %s\n", node->name);
 					/* we do not copy the node, but unlink it and then add it ot the new node

+ 5 - 0
modules/pv/pv_core.c

@@ -994,6 +994,11 @@ int pv_get_pai(struct sip_msg *msg, pv_param_t *param,
 	}
 
 	pai_body = get_pai(msg);
+	if(pai_body==NULL || pai_body->id==NULL)
+	{
+		LM_DBG("no P-Asserted-Identity header or empty body\n");
+		return pv_get_null(msg, param, res);
+	}
 	pai_uri = &pai_body->id[0];
 	cur_id = 0;
 	i = 0;

+ 5 - 5
modules/pv/pv_trans.c

@@ -126,13 +126,13 @@ static int urlencode_param(str *sin, str *sout)
 {
 	char *at, *p;
 
-	at = sout->s;
-	p  = sin->s;
-
 	if (sin==NULL || sout==NULL || sin->s==NULL || sout->s==NULL ||
 			sin->len<0 || sout->len < 3*sin->len+1)
 		return -1;
 
+	at = sout->s;
+	p  = sin->s;
+
 	while (p < sin->s+sin->len) {
 		if (isalnum(*p) || *p == '-' || *p == '_' || *p == '.' || *p == '~')
 			*at++ = *p;
@@ -1655,7 +1655,7 @@ int tr_eval_line(struct sip_msg *msg, tr_param_t *tp, int subtype,
 					if(p==NULL)
 					{
 						/* last line */
-						mv.len = (val->rs.s + val->rs.len) - p;
+						mv.len = (val->rs.s + val->rs.len) - mv.s;
 					} else {
 						mv.len = p - mv.s;
 					}
@@ -1737,7 +1737,7 @@ int tr_eval_line(struct sip_msg *msg, tr_param_t *tp, int subtype,
 					if(p==NULL)
 					{
 						/* last line */
-						mv.len = (val->rs.s + val->rs.len) - p;
+						mv.len = (val->rs.s + val->rs.len) - mv.s;
 					} else {
 						mv.len = p - mv.s;
 					}

+ 8 - 2
modules/rls/list.h

@@ -41,8 +41,11 @@ static inline list_entry_t *list_insert(str *strng, list_entry_t *list, int *dup
 	if (cmp == 0)
 	{
 		if (duplicate != NULL)
+		{
 			*duplicate = 1;
-		return list;
+			pkg_free(p);
+			return list;
+		}
 	}
 	if (cmp > 0)
 	{
@@ -58,8 +61,11 @@ static inline list_entry_t *list_insert(str *strng, list_entry_t *list, int *dup
 		if (cmp == 0)
 		{
 			if (duplicate != NULL)
+			{
 				*duplicate = 1;
-			return list;
+				pkg_free(p);
+				return list;
+			}
 		}
 
 		p->next = q->next;

+ 1 - 1
modules/rls/notify.c

@@ -1366,7 +1366,7 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 	{
 		/* No path specified - use all resource-lists. */
 		*rl_node = XMLDocGetNodeByName(*xmldoc,"resource-lists", NULL);
-		if(rl_node==NULL)
+		if(*rl_node==NULL)
 		{
 			LM_ERR("no resource-lists node in XML document\n");
 			goto error;

+ 2 - 0
modules/rls/subscribe.c

@@ -1037,6 +1037,8 @@ int send_resource_subs(char* uri, void* param)
 		LM_WARN("%.*s has %.*s multiple times in the same resource list\n",
 			s->watcher_uri->len, s->watcher_uri->s,
 			s->pres_uri->len, s->pres_uri->s);
+		pkg_free(tmp_str->s);
+		pkg_free(tmp_str);
 		return 1;
 	}
 

+ 1 - 1
modules/rls/utils.h

@@ -21,7 +21,7 @@
  *
  */
 
-#ifndef RLS_URILS_H
+#ifndef RLS_UTILS_H
 #define RLS_UTILS_H
 
 #include "../../ut.h"

+ 22 - 19
modules/sdpops/README

@@ -10,7 +10,7 @@ Daniel-Constantin Mierla
 
    <[email protected]>
 
-   Copyright (c) 2011 asipto.com
+   Copyright © 2011 asipto.com
      __________________________________________________________________
 
    Table of Contents
@@ -149,7 +149,7 @@ Chapter 1. Admin Guide
    4.17. sdp_with_ice()
    4.18. sdp_get_line_startswith(avpvar, string)
 
-4.1.  sdp_remove_codecs_by_id(list)
+4.1. sdp_remove_codecs_by_id(list)
 
    Remove the codecs provided in the parameter 'list' from all media
    streams found in SDP payload. The parameter 'list' must be one item or
@@ -166,7 +166,7 @@ sdp_remove_codecs_by_id("0");
 sdp_remove_codecs_by_id("0,8,3");
 ...
 
-4.2.  sdp_remove_codecs_by_name(list)
+4.2. sdp_remove_codecs_by_name(list)
 
    Remove the codecs provided in the parameter 'list' from all media
    streams found in SDP payload. The parameter 'list' must be one item or
@@ -183,7 +183,7 @@ sdp_remove_codecs_by_name("PCMU");
 sdp_remove_codecs_by_name("PCMU,PCMA,GSM");
 ...
 
-4.3.  sdp_remove_line_by_prefix(string)
+4.3. sdp_remove_line_by_prefix(string)
 
    Remove all SDP attribute lines beginning with 'string' in all media
    streams.
@@ -202,7 +202,7 @@ if ($si == "2001:DB8::8:800:200C:417A"
 
 ...
 
-4.4.  sdp_keep_codecs_by_id(list [, mtype])
+4.4. sdp_keep_codecs_by_id(list [, mtype])
 
    Keep only the codecs provided in the parameter 'list' from all media
    streams found in SDP payload. The parameter 'list' must be one item or
@@ -222,13 +222,16 @@ sdp_keep_codecs_by_id("0");
 sdp_keep_codecs_by_id("0,8,3", "audio");
 ...
 
-4.5.  sdp_keep_codecs_by_name(list [, mtype])
+4.5. sdp_keep_codecs_by_name(list [, mtype])
 
    Keep only the codecs provided in the parameter 'list' from all media
    streams found in SDP payload. The parameter 'list' must be one or a
    comma separated list of codec names. The parameter can be a static
    string or a variable holding the list of codec names.
 
+   Note that you have to explicitely keep 'telephone-event' in the list of
+   names if you want to keep DTMF attributes.
+
    Optional parameter mtype can be provided to apply the operations only
    to the streams matching m=mtype.
 
@@ -242,7 +245,7 @@ sdp_keep_codecs_by_name("PCMU");
 sdp_keep_codecs_by_name("PCMU,PCMA,GSM");
 ...
 
-4.6.  sdp_with_media(type)
+4.6. sdp_with_media(type)
 
    Return true of the SDP has 'media=type ...' line. Useful to check the
    content of the RTP sessions, such as 'audio' or 'video'. The parameter
@@ -259,7 +262,7 @@ if(sdp_with_media("video"))
 }
 ...
 
-4.7.  sdp_remove_media(type)
+4.7. sdp_remove_media(type)
 
    Remove the streams that match on 'm=type ...' line. The parameter can
    be static string or variable holding the media type.
@@ -272,7 +275,7 @@ if(sdp_with_media("video"))
 sdp_remove_media("video");
 ...
 
-4.8.  sdp_with_transport(type)
+4.8. sdp_with_transport(type)
 
    Return true of the SDP has 'media=media port type ...' line. Useful to
    check the transport of the RTP sessions, such as 'RTP/AVP', 'RTP/SAVP'
@@ -290,7 +293,7 @@ if(sdp_with_transport("RTP/SAVP"))
 }
 ...
 
-4.9.  sdp_with_transport_like(string)
+4.9. sdp_with_transport_like(string)
 
    Returns true if the SDP has 'media=media port type ...' line, where
    type contains string. The parameter can be static string or variable
@@ -307,7 +310,7 @@ if(sdp_with_transport_like("SAVPF"))
 }
 ...
 
-4.10.  sdp_transport(pv)
+4.10. sdp_transport(pv)
 
    Assigns common media transport (if any) of 'm' lines to pv argument.
    Returns 1 if common media transport was found, -2 if there was no
@@ -320,7 +323,7 @@ if(sdp_with_transport_like("SAVPF"))
 sdp_transport("$avp(caller_rtp_transport)");
 ...
 
-4.11.  sdp_remove_transport(type)
+4.11. sdp_remove_transport(type)
 
    Remove the streams that match on 'm=media port type ...' line. The
    parameter can be static string or variable holding the transport type.
@@ -333,7 +336,7 @@ sdp_transport("$avp(caller_rtp_transport)");
 sdp_remove_transport("RTP/AVP");
 ...
 
-4.12.  sdp_with_codecs_by_id(list)
+4.12. sdp_with_codecs_by_id(list)
 
    Returns true if any of the codecs provided in the parameter 'list' from
    all media streams is found in SDP payload. The parameter 'list' must be
@@ -350,7 +353,7 @@ if(sdp_with_codecs_by_id("0")) { ... }
 if(sdp_with_codecs_by_id("0,8,3")) { ... }
 ...
 
-4.13.  sdp_with_codecs_by_name(list)
+4.13. sdp_with_codecs_by_name(list)
 
    Returns true if any of the codecs provided in the parameter 'list' from
    all media streams is found in SDP payload. The parameter 'list' must be
@@ -367,7 +370,7 @@ if(sdp_with_codecs_by_name("PCMU")) { ... }
 if(sdp_with_codecs_by_name("PCMU,PCMA,GSM")) { ... }
 ...
 
-4.14.  sdp_print(level)
+4.14. sdp_print(level)
 
    Print the SDP internal structure to log 'level'. The parameter can be
    static integer or variable holding the integer value of the log level.
@@ -380,7 +383,7 @@ if(sdp_with_codecs_by_name("PCMU,PCMA,GSM")) { ... }
 sdp_print("1");
 ...
 
-4.15.  sdp_get(avpvar)
+4.15. sdp_get(avpvar)
 
    Store the SDP part of message body in an AVP. Return 1 if SDP is found,
    -1 on error and -2 if there is no SDP part in the message body.
@@ -392,7 +395,7 @@ sdp_print("1");
 sdp_get("$avp(sdp)");
 ...
 
-4.16.  sdp_content()
+4.16. sdp_content()
 
    Return true if the SIP message has SDP body or a SDP part in body.
 
@@ -405,7 +408,7 @@ if(sdp_content()) {
 }
 ...
 
-4.17.  sdp_with_ice()
+4.17. sdp_with_ice()
 
    Return true if the SIP message has SDP body that contains ICE candidate
    attribute(s).
@@ -419,7 +422,7 @@ if(sdp_with_ice()) {
 }
 ...
 
-4.18.  sdp_get_line_startswith(avpvar, string)
+4.18. sdp_get_line_startswith(avpvar, string)
 
    Store the search part of SDP body message with line beginning with
    'string' in an AVP. Return 1 if 'string' is found in SDP, -1 on error

+ 4 - 0
modules/sdpops/doc/sdpops_admin.xml

@@ -185,6 +185,10 @@ sdp_keep_codecs_by_id("0,8,3", "audio");
 			be one or a comma separated list of codec names. The
 			parameter can be a static string or a variable holding the
 			list of codec names.
+	    </para>
+		<para>
+			Note that you have to explicitely keep 'telephone-event' in the list
+			of names if you want to keep DTMF attributes.
 	    </para>
 		<para>
 			Optional parameter mtype can be provided to apply the operations

+ 1 - 1
modules/siputils/checks.c

@@ -354,7 +354,7 @@ int tel2sip(struct sip_msg* _msg, char* _uri, char* _hostpart, char* _res)
     if (strncasecmp(uri.s, "tel:", 4) != 0) return 1;
     
     /* reserve memory for clean tel uri */
-    tel_uri.s = pkg_malloc(uri.len);
+    tel_uri.s = pkg_malloc(uri.len+1);
     if (tel_uri.s == 0) {
 	LM_ERR("no more pkg memory\n");
 	return -1;

+ 10 - 5
modules/siputils/sipops.c

@@ -92,7 +92,7 @@ int w_cmp_aor(struct sip_msg *msg, char *uri1, char *uri2)
 
 int w_is_gruu(sip_msg_t *msg, char *uri1, char *p2)
 {
-	str s1;
+        str s1, *s2;
 	sip_uri_t turi;
 	sip_uri_t *puri;
 
@@ -103,12 +103,17 @@ int w_is_gruu(sip_msg_t *msg, char *uri1, char *p2)
 			LM_ERR("cannot get first parameter\n");
 			return -8;
 		}
-		if(parse_uri(s1.s, s1.len, &turi)!=0)
-			return -1;
+		if(parse_uri(s1.s, s1.len, &turi)!=0) {
+		    LM_ERR("parsing of uri '%.*s' failed\n", s1.len, s1.s);
+		    return -1;
+		}
 		puri = &turi;
 	} else {
-		if(parse_sip_msg_uri(msg)<0)
-			return -1;
+  	        if(parse_sip_msg_uri(msg)<0) {
+		    s2 = GET_RURI(msg);
+  		    LM_ERR("parsing of uri '%.*s' failed\n", s2->len, s2->s);
+		    return -1;
+		}
 		puri = &msg->parsed_uri;
 	}
 	if(puri->gr.s!=NULL)

Разница между файлами не показана из-за своего большого размера
+ 268 - 301
modules/textops/README


+ 140 - 0
modules/textops/doc/textops_admin.xml

@@ -1358,6 +1358,146 @@ if (starts_with("$rU", "+358"))
 		</example>
 	</section>
 
+	<section id="textops.f.set_body_multipart">
+		<title>
+		<function moreinfo="none">set_body_multipart([txt,content_type][,boundary])</function>
+		</title>
+		<para>
+		Set multipart body to a SIP message. If called with no parameters, will convert
+		present body to multipart.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para><emphasis>txt</emphasis> - text for the body, can include
+				pseudo-variables.
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>content_type</emphasis> - value of Content-Type header,
+				can include pseudo-variables.
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>boundary</emphasis> - string to use as boundary,
+				can include pseudo-variables. Default: unique-boundary-1
+			</para>
+		</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<para>
+		The core will take care of the last boundary ending "--". Detecting wich one is
+		the last and fixing the others if needed.
+		</para>
+		<example>
+		<title><function>set_body_multipart</function> usage</title>
+		<programlisting format="linespecific">
+...
+set_body_multipart("test", "text/plain", "delimiter");
+...
+Will produce:
+...
+Content-Type: multipart/mixed;boundary="delimiter"
+Mime-Version: 1.0
+
+--delimiter
+Content-Type: text/plain
+
+text
+
+--delimiter
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="textops.f.append_body_part">
+		<title>
+		<function moreinfo="none">append_body_part(txt,content_type[, content_disposition])</function>
+		</title>
+		<para>
+		Append a part on multipart body SIP message. Will use "unique-boundary-1" as boundary.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para><emphasis>txt</emphasis> - text for the multipart body, can include
+				pseudo-variables.
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>content_type</emphasis> - value of Content-Type header,
+				can include pseudo-variables.
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>content_disposition</emphasis> - value of Content-Disposition header,
+				can include pseudo-variables.
+			</para>
+		</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<para>
+		The core will take care of the last boundary ending "--". Detecting wich one is
+		the last and fixing the others if needed.
+		</para>
+		<example>
+		<title><function>append_body_part</function> usage</title>
+		<programlisting format="linespecific">
+...
+$var(b) = "7e Od 04 55 75 69 20 4d 61 6b 65 43 61 6c 6c"
+append_body_part("$var(b)", "application/vnd.cirpack.isdn-ext", "signal;handling=required");
+...
+Will append this the body:
+...
+Content-Type: application/vnd.cirpack.isdn-ext
+Content-Disposition: signal;handling=required
+
+7e Od 04 55 75 69 20 4d 61 6b 65 43 61 6c 6c
+
+--unique-boundary-1
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="textops.f.remove_body_part">
+		<title>
+		<function moreinfo="none">remove_body_part(content_type)</function>
+		</title>
+		<para>
+		Remove a part on a multipart body SIP message.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para><emphasis>content_type</emphasis> - value of Content-Type header
+				of the part to be removed. If more than one exists the first
+				occurrence will be removed.
+			</para>
+		</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<para>
+		The core will take care of the last boundary ending "--". Detecting wich one is
+		the last and fixing the others if needed.
+		</para>
+		<example>
+		<title><function>remove_body_part</function> usage</title>
+		<programlisting format="linespecific">
+...
+remove_body_part("application/vnd.cirpack.isdn-ext");
+...
+</programlisting>
+		</example>
+	</section>
+
 	</section>
 	<section>
 		<title>Known Limitations</title>

+ 673 - 94
modules/textops/textops.c

@@ -123,6 +123,14 @@ static int append_time_f(struct sip_msg* msg, char* , char *);
 static int append_time_request_f(struct sip_msg* msg, char* , char *);
 static int set_body_f(struct sip_msg* msg, char*, char *);
 static int set_rpl_body_f(struct sip_msg* msg, char*, char *);
+static int set_multibody_0(struct sip_msg* msg, char*, char *, char *);
+static int set_multibody_1(struct sip_msg* msg, char*, char *, char *);
+static int set_multibody_2(struct sip_msg* msg, char*, char *, char *);
+static int set_multibody_3(struct sip_msg* msg, char*, char *, char *);
+static int append_multibody_2(struct sip_msg* msg, char*, char *);
+static int append_multibody_3(struct sip_msg* msg, char*, char *, char *);
+static int fixup_multibody_f(void** param, int param_no);
+static int remove_multibody_f(struct sip_msg *msg, char *);
 static int is_method_f(struct sip_msg* msg, char* , char *);
 static int has_body_f(struct sip_msg *msg, char *type, char *str2 );
 static int in_list_f(struct sip_msg* _msg, char* _subject, char* _list,
@@ -275,6 +283,27 @@ static cmd_export_t cmds[]={
 
 	{"bind_textops",      (cmd_function)bind_textops,       0, 0, 0,
 		0},
+	{"set_body_multipart",         (cmd_function)set_multibody_0,        0,
+		0, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"set_body_multipart",         (cmd_function)set_multibody_1,        1,
+		fixup_spve_null, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"set_body_multipart",         (cmd_function)set_multibody_2,        2,
+		fixup_spve_spve, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"set_body_multipart",         (cmd_function)set_multibody_3,        3,
+		fixup_multibody_f, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"append_body_part",     (cmd_function)append_multibody_2,    2,
+		fixup_spve_spve, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"append_body_part",     (cmd_function)append_multibody_3,    3,
+		fixup_multibody_f, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"remove_body_part",     (cmd_function)remove_multibody_f,    1,
+		fixup_spve_null, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 
 	{0,0,0,0,0,0}
 };
@@ -866,6 +895,23 @@ static inline int find_line_start(char *text, unsigned int text_len,
     return 0;
 }
 
+/**
+ * return:
+ *  1: multipart
+ */
+static int check_multipart(struct sip_msg *msg)
+{
+	int mime;
+
+	/* the function search for and parses the Content-Type hdr */
+	mime = parse_content_type_hdr (msg);
+	if(mime<0) {
+		LM_ERR("failed to extract content type hdr\n");
+		return -1;
+	}
+	if(mime!=MIMETYPE(MULTIPART,MIXED)) return 0;
+	return 1;
+}
 
 /* Filters multipart/mixed body by leaving out everything else except
  * first body part of given content type. */
@@ -874,10 +920,8 @@ static int filter_body_f(struct sip_msg* msg, char* _content_type,
 {
 	char *start;
 	unsigned int len;
-	str *content_type, body, params, boundary;
-	param_hooks_t hooks;
-	param_t *p, *list;
-	unsigned int mime;
+	str *content_type, body;
+	str boundary = {0,0};
 
 	body.s = get_body(msg);
 	if (body.s == 0) {
@@ -889,106 +933,73 @@ static int filter_body_f(struct sip_msg* msg, char* _content_type,
 		LM_DBG("message body has zero length\n");
 		return -1;
 	}
-	
-	content_type = (str *)_content_type;
 
-	mime = parse_content_type_hdr(msg);
-	if (mime <= 0) {
-	    LM_ERR("failed to parse Content-Type hdr\n");
-	    return -1;
-	}
-	if (mime != ((TYPE_MULTIPART << 16) + SUBTYPE_MIXED)) {
-	    LM_ERR("content type is not multipart/mixed\n");
-	    return -1;
-	}
-
-	params.s = memchr(msg->content_type->body.s, ';', 
-			  msg->content_type->body.len);
-	if (params.s == NULL) {
-	    LM_ERR("Content-Type hdr has no params\n");
-	    return -1;
-	}
-	params.len = msg->content_type->body.len - 
-	    (params.s - msg->content_type->body.s);
-	if (parse_params(&params, CLASS_ANY, &hooks, &list) < 0) {
-	    LM_ERR("while parsing Content-Type params\n");
-	    return -1;
-	}
-	boundary.s = NULL;
-	boundary.len = 0;
-	for (p = list; p; p = p->next) {
-	    if ((p->name.len == 8)
-		&& (strncasecmp(p->name.s, "boundary", 8) == 0)) {
-		boundary.s = pkg_malloc(p->body.len + 2);
-		if (boundary.s == NULL) {
-		    free_params(list);
-		    LM_ERR("no memory for boundary string\n");
-		    return -1;
-		}
-		*(boundary.s) = '-';
-		*(boundary.s + 1) = '-';
-		memcpy(boundary.s + 2, p->body.s, p->body.len);
-		boundary.len = 2 + p->body.len;
-		LM_DBG("boundary is <%.*s>\n", boundary.len, boundary.s);
-		break;
-	    }
+	if(check_multipart(msg)!=1) {
+		LM_WARN("body not multipart\n");
+		return -1;
 	}
-	free_params(list);
-	if (boundary.s == NULL) {
-	    LM_ERR("no mandatory param \";boundary\"\n");
-	    return -1;
+	if(get_boundary(msg, &boundary)!=0) {
+		return -1;
 	}
-	
+	content_type = (str *)_content_type;
 	start = body.s;
 	len = body.len;
-	
-	while (find_line_start("Content-Type: ", 14, &start, &len)) {
-	    start = start + 14;
-	    len = len - 14;
-	    if (len > content_type->len + 2) {
-		if (strncasecmp(start, content_type->s, content_type->len)
-		    == 0) {
-		    LM_DBG("found content type %.*s\n",
-			    content_type->len, content_type->s);
-		    start = start + content_type->len;
-		    if ((*start != 13) || (*(start + 1) != 10)) {
-			LM_ERR("no CRLF found after content type\n");
-			goto err;
-		    }
-		    start = start + 2;
-		    len = len - content_type->len - 2;
-		    while ((len > 0) && ((*start == 13) || (*start == 10))) {
-			len = len - 1;
-			start = start + 1;
-		    }
-		    if (del_lump(msg, body.s - msg->buf, start - body.s, 0)
-			== 0) {
-			LM_ERR("deleting lump <%.*s> failed\n",
-			       (int)(start - body.s), body.s);
-			goto err;
-		    }
-		    if (find_line_start(boundary.s, boundary.len, &start,
-					&len)) { 
-			if (del_lump(msg, start - msg->buf, len, 0) == 0) {
-			    LM_ERR("deleting lump <%.*s> failed\n",
-				   len, start);
-			    goto err;
-			} else {
-			    pkg_free(boundary.s);
-			    return 1;
+
+	while (find_line_start("Content-Type: ", 14, &start, &len))
+	{
+		start = start + 14;
+		len = len - 14;
+		LM_DBG("line: [%.*s]\n", len, start);
+		if (len > content_type->len + 2) {
+			if (strncasecmp(start, content_type->s, content_type->len)== 0)
+			{
+				LM_DBG("found content type %.*s\n",
+					content_type->len, content_type->s);
+				start = start + content_type->len;
+				if ((*start != 13) || (*(start + 1) != 10))
+				{
+					LM_ERR("no CRLF found after content type\n");
+					goto err;
+				}
+				start = start + 2;
+				len = len - content_type->len - 2;
+				while ((len > 0) && ((*start == 13) || (*start == 10)))
+				{
+					len = len - 1;
+					start = start + 1;
+				}
+				if (del_lump(msg, body.s - msg->buf, start - body.s, 0)== 0)
+				{
+					LM_ERR("deleting lump <%.*s> failed\n",
+					(int)(start - body.s), body.s);
+					goto err;
+				}
+				if (find_line_start(boundary.s, boundary.len, &start,
+					&len))
+				{
+					if (del_lump(msg, start - msg->buf, len, 0) == 0)
+					{
+						LM_ERR("deleting lump <%.*s> failed\n", len, start);
+						goto err;
+					}
+					else
+					{
+						pkg_free(boundary.s);
+						return 1;
+					}
+				}
+				else
+				{
+					LM_ERR("boundary not found after content\n");
+					goto err;
+				}
 			}
-		    } else {
-			LM_ERR("boundary not found after content\n");
+		} else {
 			goto err;
-		    }
 		}
-	    } else {
-		pkg_free(boundary.s);
-		return -1;
-	    }
 	}
  err:
-	pkg_free(boundary.s);
+	if(boundary.s) pkg_free(boundary.s);
 	return -1;
 }
 
@@ -1444,7 +1455,575 @@ static int set_rpl_body_f(struct sip_msg* msg, char* p1, char* p2)
 	return 1;
 }
 
+static str* generate_boundary(str txt, str content_type,
+	str content_disposition, str delimiter, unsigned int initial)
+{
+	unsigned int i = 0;
+	str cth = {"Content-Type: ", 14};
+	str cdh = {"Content-Disposition: ", 21};
+	str* n;
+	unsigned int flag = 0;
 
+	if(txt.len==0||content_type.len==0||delimiter.len==0)
+	{
+		LM_ERR("invalid parameters\n");
+		return NULL;
+	}
+	n = pkg_malloc(sizeof(str));
+	if(n==NULL)
+	{
+		LM_ERR("out of pkg memory\n");
+		return NULL;
+	}
+	n->len = delimiter.len + 2 + CRLF_LEN;
+	if(initial) n->len = 2*n->len;
+	if(strncmp("\r\n\r\n", txt.s+txt.len-4,4)!=0)
+	{
+		n->len = n->len + CRLF_LEN;
+		flag = 1;
+		LM_DBG("adding final CRLF+CRLF\n");
+	}
+	n->len=n->len + cth.len + content_type.len + 2*CRLF_LEN;
+	if(content_disposition.len>0)
+	{
+		n->len = n->len + cdh.len + content_disposition.len + CRLF_LEN;
+	}
+	n->len = n->len + txt.len;
+
+	n->s = pkg_malloc(sizeof(char)*(n->len));
+	if(n->s==0)
+	{
+		LM_ERR("out of pkg memory\n");
+		pkg_free(n);
+		return NULL;
+	}
+	if(initial)
+	{
+		memcpy(n->s, "--", 2); i=2;
+		memcpy(n->s+i, delimiter.s, delimiter.len); i=i+delimiter.len;
+		memcpy(n->s+i, CRLF, CRLF_LEN); i=i+CRLF_LEN;
+	}
+
+	memcpy(n->s+i, cth.s, cth.len); i=i+cth.len;
+	memcpy(n->s+i, content_type.s, content_type.len); i=i+content_type.len;
+	memcpy(n->s+i, CRLF, CRLF_LEN); i=i+CRLF_LEN;
+
+	if(content_disposition.len>0)
+	{
+		memcpy(n->s+i, cdh.s, cdh.len); i=i+cdh.len;
+		memcpy(n->s+i, content_disposition.s, content_disposition.len);
+		i=i+content_disposition.len;
+		memcpy(n->s+i, CRLF, CRLF_LEN); i=i+CRLF_LEN;
+	}
+	memcpy(n->s+i, CRLF, CRLF_LEN); i=i+CRLF_LEN;
+
+	memcpy(n->s+i, txt.s, txt.len); i=i+txt.len;
+	if(flag) { memcpy(n->s+i, CRLF, CRLF_LEN); i=i+CRLF_LEN; }
+
+	memcpy(n->s+i, "--", 2); i=i+2;
+	memcpy(n->s+i, delimiter.s, delimiter.len); i=i+delimiter.len;
+	memcpy(n->s+i, CRLF, CRLF_LEN); i=i+CRLF_LEN;
+
+	if(i!=n->len)
+	{
+		LM_ERR("out of bounds\n");
+	}
+	return n;
+}
+
+int set_multibody_helper(struct sip_msg* msg, char* p1, char* p2, char* p3)
+{
+	struct lump *anchor;
+	char* buf = NULL;
+	int len;
+	char* value_s;
+	int value_len;
+	str body = {0,0};
+	str nb = {0,0};
+	str oc = {0,0};
+	str cd = {0,0};
+	str delimiter = {0,0};
+	str default_delimiter = {"unique-boundary-1", 17};
+	str nc = {0,0};
+	str cth = {"Content-Type: ", 14};
+	str* nbb;
+	unsigned int convert = 0;
+	fparam_t header;
+	header.orig = NULL;
+	header.type = FPARAM_STR;
+	header.v.str.s = "Mime-Version: 1.0\r\n";
+	header.v.str.len = 19;
+
+	if(p3==0)
+	{
+		delimiter.s = default_delimiter.s;
+		delimiter.len = default_delimiter.len;
+	}
+	else
+	{
+		if(fixup_get_svalue(msg, (gparam_p)p3, &delimiter)!=0)
+		{
+			LM_ERR("unable to get p3\n");
+			return -1;
+		}
+		if(delimiter.s==NULL || delimiter.len == 0)
+		{
+			LM_ERR("invalid boundary parameter\n");
+			return -1;
+		}
+	}
+	LM_DBG("delimiter<%d>:[%.*s]\n", delimiter.len, delimiter.len, delimiter.s);
+	if(p1==0 || p2==0)
+	{
+		if(check_multipart(msg)==1) {
+			LM_WARN("body is already multipart. Do nothing\n");
+			return -1;
+		}
+		convert = 1;
+	}
+	else
+	{
+		if(fixup_get_svalue(msg, (gparam_p)p1, &nb)!=0)
+		{
+			LM_ERR("unable to get p1\n");
+			return -1;
+		}
+		if(nb.s==NULL || nb.len == 0)
+		{
+			LM_ERR("invalid body parameter\n");
+			return -1;
+		}
+		if(fixup_get_svalue(msg, (gparam_p)p2, &oc)!=0)
+		{
+			LM_ERR("unable to get p2\n");
+			return -1;
+		}
+		if(oc.s==NULL || oc.len==0)
+		{
+			LM_ERR("invalid content-type parameter\n");
+			return -1;
+		}
+		if(check_multipart(msg)==1) {
+			convert = -1;
+		}
+	}
+
+	body.len = 0;
+	body.s = get_body(msg);
+	if(body.s==0)
+	{
+		LM_ERR("malformed sip message\n");
+		return -1;
+	}
+	body.len = msg->len -(int)(body.s-msg->buf);
+
+	del_nonshm_lump( &(msg->body_lumps) );
+	msg->body_lumps = NULL;
+
+	if(msg->content_length)
+	{
+		if(body.len > 0)
+		{
+			if(body.s+body.len>msg->buf+msg->len)
+			{
+				LM_ERR("invalid content length: %d\n", body.len);
+				return -1;
+			}
+			if(convert==1)
+			{
+				/* need to copy body */
+				nb.s=pkg_malloc(sizeof(char)*body.len);
+				if (nb.s==0)
+				{
+					LM_ERR("out of pkg memory\n");
+					return -1;
+				}
+				memcpy(nb.s, body.s, body.len);
+				nb.len = body.len;
+				if(msg->content_type!=NULL && msg->content_type->body.s!=NULL)
+				{
+					oc.len = msg->content_type->body.len;
+					oc.s=pkg_malloc(sizeof(char)*oc.len);
+					if (oc.s==0)
+					{
+						LM_ERR("out of pkg memory\n");
+						goto error;
+					}
+					memcpy(oc.s, msg->content_type->body.s, oc.len);
+				}
+			}
+			if(del_lump(msg, body.s-msg->buf, body.len, 0) == 0)
+			{
+				LM_ERR("cannot delete existing body");
+				goto error;
+			}
+		}
+	}
+
+	anchor = anchor_lump(msg, msg->unparsed-msg->buf, 0, 0);
+	if(anchor==0)
+	{
+		LM_ERR("failed to get anchor\n");
+		goto error;
+	}
+
+	/* get initial boundary */
+	nbb = generate_boundary(nb, oc, cd, delimiter, 1);
+	if(nbb==NULL)
+	{
+		LM_ERR("couldn't create initial boundary\n");
+		goto error;
+	}
+
+	if(msg->content_length==0)
+	{
+		/* need to add Content-Length */
+		len = nbb->len;
+		value_s=int2str(len, &value_len);
+
+		len=CONTENT_LENGTH_LEN+value_len+CRLF_LEN;
+		buf=pkg_malloc(sizeof(char)*len);
+
+		if (buf==0)
+		{
+			LM_ERR("out of pkg memory\n");
+			goto error;
+		}
+
+		memcpy(buf, CONTENT_LENGTH, CONTENT_LENGTH_LEN);
+		memcpy(buf+CONTENT_LENGTH_LEN, value_s, value_len);
+		memcpy(buf+CONTENT_LENGTH_LEN+value_len, CRLF, CRLF_LEN);
+		if (insert_new_lump_after(anchor, buf, len, 0) == 0)
+		{
+			LM_ERR("failed to insert content-length lump\n");
+			goto error;
+		}
+		buf = NULL;
+	}
+
+	if(convert!=-1)
+	{
+		/* set new content type with delimiter */
+		nc.len = delimiter.len + 27;
+		nc.s = pkg_malloc(sizeof(char)*nc.len);
+		memcpy(nc.s, "multipart/mixed;boundary=\"", 26);
+		memcpy(nc.s+26, delimiter.s, delimiter.len);
+		nc.s[26+delimiter.len] = '"';
+		LM_DBG("content-type<%d>:[%.*s]\n", nc.len, nc.len, nc.s);
+		/* add content-type */
+		if(msg->content_type==NULL || msg->content_type->body.len!=nc.len
+				|| strncmp(msg->content_type->body.s, nc.s, nc.len)!=0)
+		{
+			if(msg->content_type!=NULL)
+				if(del_lump(msg, msg->content_type->name.s-msg->buf,
+							msg->content_type->len, 0) == 0)
+				{
+					LM_ERR("failed to delete content type\n");
+					goto error;
+				}
+			value_len = nc.len;
+			len = cth.len + value_len + CRLF_LEN;
+			buf = pkg_malloc(sizeof(char)*len);
+
+			if(buf==0)
+			{
+				LM_ERR("out of pkg memory\n");
+				goto error;
+			}
+			memcpy(buf, cth.s, cth.len);
+			memcpy(buf + cth.len, nc.s, value_len);
+			memcpy(buf + cth.len + value_len, CRLF, CRLF_LEN);
+			if (insert_new_lump_after(anchor, buf, len, 0) == 0)
+			{
+				LM_ERR("failed to insert content-type lump\n");
+				goto error;
+			}
+			buf = NULL;
+		}
+		/* add Mime-Version header */
+		if(add_hf_helper(msg, 0, 0, &header, 0, 0)<0)
+		{
+			LM_ERR("failed to add Mime-Version header\n");
+			goto error;
+		}
+	}
+	anchor = anchor_lump(msg, body.s - msg->buf, 0, 0);
+	if(anchor==0)
+	{
+		LM_ERR("failed to get body anchor\n");
+		goto error;
+	}
+
+	if(insert_new_lump_after(anchor, nbb->s, nbb->len, 0)==0)
+	{
+		LM_ERR("failed to insert body lump\n");
+		goto error;
+	}
+	pkg_free(nbb);
+	if(nc.s!=NULL) pkg_free(nc.s);
+	LM_DBG("set flag FL_BODY_MULTIPART\n");
+	msg->msg_flags |= FL_BODY_MULTIPART;
+	return 1;
+
+error:
+	if(nbb!=NULL) { pkg_free(nbb->s); pkg_free(nbb); }
+	if(nc.s!=NULL) pkg_free(nc.s);
+	if(buf!=NULL) pkg_free(buf);
+	if(convert && nb.s!=NULL) pkg_free(nb.s);
+	if(convert && oc.s!=NULL) pkg_free(oc.s);
+	return -1;
+}
+
+static int set_multibody_0(struct sip_msg* msg, char* p1, char* p2, char* p3)
+{
+	return set_multibody_helper(msg, NULL, NULL, NULL);
+}
+
+static int set_multibody_1(struct sip_msg* msg, char* p1, char* p2, char* p3)
+{
+	return set_multibody_helper(msg, NULL, NULL, p1);
+}
+
+static int set_multibody_2(struct sip_msg* msg, char* p1, char* p2, char* p3)
+{
+	return set_multibody_helper(msg, p1, p2, NULL);
+}
+
+static int set_multibody_3(struct sip_msg* msg, char* p1, char* p2, char *p3)
+{
+	return set_multibody_helper(msg, p1, p2, p3);
+}
+
+int append_multibody_helper(struct sip_msg* msg, char* p1, char* p2, char* p3)
+{
+	struct lump *l;
+	int off;
+	str body = {0,0};
+	str nc = {0,0};
+	str cd = {0,0};
+	str txt = {0,0};
+	str* nbb = NULL;
+	str delimiter = {0,0};
+
+	if(p1==0 || p2==0)
+	{
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_p)p1, &txt)!=0)
+	{
+		LM_ERR("unable to get p1\n");
+		return -1;
+	}
+	if(txt.s==NULL || txt.len==0)
+	{
+		LM_ERR("invalid body parameter\n");
+		return -1;
+	}
+	if(fixup_get_svalue(msg, (gparam_p)p2, &nc)!=0)
+	{
+		LM_ERR("unable to get p2\n");
+		return -1;
+	}
+	if(nc.s==NULL || nc.len==0)
+	{
+		LM_ERR("invalid content-type parameter\n");
+		return -1;
+	}
+	if(p3!=NULL)
+	{
+		if(fixup_get_svalue(msg, (gparam_p)p3, &cd)!=0)
+		{
+			LM_ERR("unable to get p3\n");
+			return -1;
+		}
+	}
+
+	body.s = get_body(msg);
+	if(body.s==0) {
+		LM_ERR("failed to get the message body\n");
+		return -1;
+	}
+	body.len = msg->len -(int)(body.s-msg->buf);
+	if(body.len==0) {
+		LM_DBG("message body has zero length\n");
+		return -1;
+	}
+
+	off=body.s-msg->buf;
+	if((l=anchor_lump(msg, off+body.len, 0, 0))==0)
+	{
+		LM_ERR("WTF\n");
+		return -1;
+	}
+	/* get boundary */
+	if(get_boundary(msg, &delimiter)!=0) {
+		LM_ERR("Cannot get boundary. Is body multipart?\n");
+		return -1;
+	}
+	nbb = generate_boundary(txt, nc, cd, delimiter, 0);
+	if(nbb==NULL)
+	{
+		LM_ERR("couldn't create initial boundary\n");
+		pkg_free(delimiter.s);
+		return -1;
+	}
+	pkg_free(delimiter.s);
+	if(insert_new_lump_after(l, nbb->s, nbb->len, 0)==0){
+		LM_ERR("could not insert new lump\n");
+		pkg_free(nbb->s); pkg_free(nbb);
+		return -1;
+	}
+	pkg_free(nbb);
+	if(!(msg->msg_flags&FL_BODY_MULTIPART))
+	{
+		LM_DBG("set flag FL_BODY_MULTIPART\n");
+		msg->msg_flags |= FL_BODY_MULTIPART;
+	}
+	return 1;
+}
+
+static int append_multibody_2(struct sip_msg* msg, char* p1, char* p2)
+{
+	return append_multibody_helper(msg, p1, p2, NULL);
+}
+
+static int append_multibody_3(struct sip_msg* msg, char* p1, char* p2, char *p3)
+{
+	return append_multibody_helper(msg, p1, p2, p3);
+}
+
+static int fixup_multibody_f(void** param, int param_no)
+{
+	int ret;
+	fparam_t* fp;
+
+	if(param_no<=3){
+		if((ret=fix_param_types(FPARAM_PVE, param))<0){
+			ERR("Cannot convert function parameter %d to spve \n",
+					param_no);
+			return E_UNSPEC;
+		} else {
+			fp=(fparam_t*)*param;
+			if((ret==0) && (fp->v.pve->spec==0
+						|| fp->v.pve->spec->getf==0)){
+				fparam_free_restore(param);
+				return fix_param_types(FPARAM_STR, param);
+			} else if(ret==1)
+				return fix_param_types(FPARAM_STR, param);
+			return ret;
+		}
+	} else {
+		LM_ERR("wrong number of parameters\n");
+		return E_UNSPEC;
+	}
+}
+
+static inline int get_line(char *s, int len)
+{
+	char *ch;
+
+	if ((ch = memchr(s, 13, len))) {
+		if (*(ch + 1) != 10) {
+			LM_ERR("No LF after CR\n");
+			return 0;
+		}
+		return ch - s + 2;
+	} else {
+		LM_ERR("No CRLF found\n");
+		return len;
+	}
+	return 0;
+}
+
+static int remove_multibody_f(struct sip_msg* msg, char* p1)
+{
+	char *start, *end;
+	unsigned int len, t;
+	str content_type, body;
+	str boundary = {0,0};
+
+	if(p1==0)
+	{
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_p)p1, &content_type)!=0)
+	{
+		LM_ERR("unable to get p1\n");
+		return -1;
+	}
+
+	body.s = get_body(msg);
+	if (body.s == 0) {
+		LM_ERR("failed to get the message body\n");
+		return -1;
+	}
+	body.len = msg->len - (int)(body.s - msg->buf);
+	if (body.len == 0) {
+		LM_DBG("message body has zero length\n");
+		return -1;
+	}
+
+	if(get_boundary(msg, &boundary)!=0) {
+		LM_ERR("Cannot get boundary. Is body multipart?\n");
+		return -1;
+	}
+
+	start = body.s;
+	len = body.len;
+
+	while (find_line_start("Content-Type: ", 14, &start, &len))
+	{
+		end = start + 14;
+		len = len - 14;
+		if (len > (content_type.len + 2)) {
+			if (strncasecmp(end, content_type.s, content_type.len)== 0)
+			{
+				LM_DBG("found content type %.*s\n",
+					content_type.len, content_type.s);
+				end = end + content_type.len;
+				if ((*end != 13) || (*(end + 1) != 10))
+				{
+					LM_ERR("no CRLF found after content type\n");
+					goto err;
+				}
+				end = end + 2;
+				len = len - content_type.len - 2;
+				if (find_line_start(boundary.s, boundary.len, &end,
+					&len))
+				{
+					LM_DBG("found boundary %.*s\n", boundary.len, boundary.s);
+					end = end + boundary.len;
+					len = len - boundary.len;
+					if (!(t = get_line(end, len))) goto err;
+					end += t; len = end-start;
+					if (del_lump(msg, start - msg->buf, len, 0) == 0)
+					{
+						LM_ERR("deleting lump <%.*s> failed\n", len, start);
+						goto err;
+					}
+					pkg_free(boundary.s);
+					if(!(msg->msg_flags&FL_BODY_MULTIPART))
+					{
+						LM_DBG("set flag FL_BODY_MULTIPART\n");
+						msg->msg_flags |= FL_BODY_MULTIPART;
+					}
+					return 1;
+				}
+				LM_ERR("boundary not found after content\n");
+				goto err;
+			}
+			start = end;
+		}
+		else goto err;
+	}
+ err:
+	pkg_free(boundary.s);
+	return -1;
+}
 
 static int append_to_reply_f(struct sip_msg* msg, char* key, char* str0)
 {

+ 1 - 0
modules/tls/tls_mod.c

@@ -348,6 +348,7 @@ static int mod_init(void)
 #ifndef OPENSSL_NO_DH
 	LM_INFO("With Diffie Hellman\n");
 #endif
+	tls_lookup_event_routes();
 	return 0;
 error:
 	destroy_tls_h();

+ 10 - 0
modules/tls/tls_select.c

@@ -101,11 +101,21 @@ enum {
 
 
 
+static struct tcp_connection* _tls_pv_con = 0;
 
 
+void tls_set_pv_con(struct tcp_connection *c)
+{
+	_tls_pv_con = c;
+}
+
 struct tcp_connection* get_cur_connection(struct sip_msg* msg)
 {
 	struct tcp_connection* c;
+
+	if(_tls_pv_con != 0)
+		return _tls_pv_con;
+
 	if (msg->rcv.proto != PROTO_TLS) {
 		ERR("Transport protocol is not TLS (bug in config)\n");
 		return 0;

+ 3 - 0
modules/tls/tls_select.h

@@ -43,9 +43,12 @@
 
 #include "../../select.h"
 #include "../../pvar.h"
+#include "../../tcp_conn.h"
 
 extern select_row_t tls_sel[];
 
 extern pv_export_t tls_pv[];
 
+void tls_set_pv_con(struct tcp_connection *c);
+
 #endif /* _TLS_SELECT_H */

+ 46 - 0
modules/tls/tls_server.c

@@ -46,16 +46,22 @@
 #include "../../tcp_int_send.h"
 #include "../../tcp_read.h"
 #include "../../cfg/cfg.h"
+#include "../../route.h"
+#include "../../forward.h"
+#include "../../onsend.h"
 
 #include "tls_init.h"
 #include "tls_domain.h"
 #include "tls_util.h"
 #include "tls_mod.h"
 #include "tls_server.h"
+#include "tls_select.h"
 #include "tls_bio.h"
 #include "tls_dump_vf.h"
 #include "tls_cfg.h"
 
+int tls_run_event_routes(struct tcp_connection *c);
+
 /* low memory treshold for openssl bug #1491 workaround */
 #define LOW_MEM_NEW_CONNECTION_TEST() \
 	(cfg_get(tls, tls_cfg, low_mem_threshold1) && \
@@ -435,6 +441,7 @@ int tls_connect(struct tcp_connection *c, int* error)
 			LOG(tls_log, "tls_connect: server did not "
 							"present a certificate\n");
 		}
+		tls_run_event_routes(c);
 	} else { /* 0 or < 0 */
 		*error = SSL_get_error(ssl, ret);
 	}
@@ -1343,3 +1350,42 @@ bug:
 					c, flags, ssl_read, *flags);
 	return -1;
 }
+
+
+static int _tls_evrt_connection_out = -1; /* default disabled */
+
+/*!
+ * lookup tls event routes
+ */
+void tls_lookup_event_routes(void)
+{
+	_tls_evrt_connection_out=route_lookup(&event_rt, "tls:connection-out");
+	if (_tls_evrt_connection_out>=0 && event_rt.rlist[_tls_evrt_connection_out]==0)
+		_tls_evrt_connection_out=-1; /* disable */
+	if(_tls_evrt_connection_out!=-1)
+		forward_set_send_info(1);
+}
+
+/**
+ *
+ */
+int tls_run_event_routes(struct tcp_connection *c)
+{
+	int backup_rt;
+	struct run_act_ctx ctx;
+	sip_msg_t tmsg;
+
+	if(_tls_evrt_connection_out<0)
+		return 0;
+	if(p_onsend==0 || p_onsend->msg==0)
+		return 0;
+
+	backup_rt = get_route_type();
+	set_route_type(LOCAL_ROUTE);
+	init_run_actions_ctx(&ctx);
+	tls_set_pv_con(c);
+	run_top_route(event_rt.rlist[_tls_evrt_connection_out], &tmsg, 0);
+	tls_set_pv_con(0);
+	set_route_type(backup_rt);
+	return 0;
+}

+ 2 - 0
modules/tls/tls_server.h

@@ -93,4 +93,6 @@ int tls_h_fix_read_conn(struct tcp_connection *c);
 
 int tls_connect(struct tcp_connection *c, int* error);
 int tls_accept(struct tcp_connection *c, int* error);
+
+void tls_lookup_event_routes(void);
 #endif /* _TLS_SERVER_H */

+ 1 - 1
modules/topoh/th_msg.c

@@ -136,7 +136,7 @@ int th_get_uri_type(str *uri, int *mode, str *value)
 			return -1;
 		return 2; /* decode */
 	} else {
-		if(check_self(&puri.host, (puri.port_no)?puri.port_no:SIP_PORT, 0)==1)
+		if(check_self(&puri.host, (puri.port_no)?puri.port_no:0, 0)==1)
 		{
 			/* myself -- matched on all protos */
 			ret = th_get_param_value(&puri.params, &r2, value);

+ 30 - 11
modules/utils/README

@@ -4,7 +4,13 @@ Juha Heinanen
 
    TutPro Inc.
 
-   Copyright © 2008-2009 Juha Heinanen
+Carsten Bock
+
+   ng-voice GmbH
+
+   Copyright (c) 2008-2009 Juha Heinanen
+
+   Copyright (c) 2013 Carsten Bock, ng-voice GmbH
      __________________________________________________________________
 
    Table of Contents
@@ -26,7 +32,7 @@ Juha Heinanen
 
         4. Functions
 
-              4.1. http_query(url, result)
+              4.1. http_query(url, [post-data], result)
               4.2. xcap_auth_status(watcher_uri, presentity_uri)
 
         5. MI Commands
@@ -70,7 +76,7 @@ Chapter 1. Admin Guide
 
    4. Functions
 
-        4.1. http_query(url, result)
+        4.1. http_query(url, [post-data], result)
         4.2. xcap_auth_status(watcher_uri, presentity_uri)
 
    5. MI Commands
@@ -174,17 +180,20 @@ modparam("utils", "xcap_table", "pres_xcap")
 
 4. Functions
 
-   4.1. http_query(url, result)
+   4.1. http_query(url, [post-data], result)
    4.2. xcap_auth_status(watcher_uri, presentity_uri)
 
-4.1. http_query(url, result)
+4.1.  http_query(url, [post-data], result)
 
-   Sends HTTP GET request according to URL given in "url" parameter, which
-   is a string that may contain pseudo variables.
+   Sends HTTP GET or POST request according to URL given in "url"
+   parameter, which is a string that may contain pseudo variables.
 
-   If HTTP server returns a class 2xx or 3xx reply, the first line of the
-   reply's body (if any) is stored in "result" parameter, which must be a
-   writable pseudo variable.
+   If you want to make a POST-Request, you have to define the "post"-data,
+   that should be submitted in that request as the second parameter.
+
+   If HTTP server returns a class 2xx, 3xx or 4xx reply, the first line of
+   the reply's body (if any) is stored in "result" parameter, which must
+   be a writable pseudo variable.
 
    Function returns reply code of HTTP reply or -1 if something went
    wrong.
@@ -194,6 +203,7 @@ modparam("utils", "xcap_table", "pres_xcap")
 
    Example 1.5. http_query() usage
 ...
+# GET-Request
 http_query("http://tutpro.com/index.php?r_uri=$(ru{s.escape.param})&f_uri=$(fu{s
 .escape.param})",
            "$var(result)")
@@ -201,8 +211,17 @@ switch ($retcode) {
        ...
 }
 ...
+...
+# POST-Request
+http_query("http://tutpro.com/index.php", "r_uri=$(ru{s.escape.param})&f_uri=$(f
+u{s.escape.param})",
+           "$var(result)")
+switch ($retcode) {
+       ...
+}
+...
 
-4.2. xcap_auth_status(watcher_uri, presentity_uri)
+4.2.  xcap_auth_status(watcher_uri, presentity_uri)
 
    Function checks in the presence server database if a watcher is
    authorized to subscribe to event "presence" of presentity. Sphere

+ 1 - 1
modules/utils/doc/utils_admin.xml

@@ -171,7 +171,7 @@ modparam("utils", "xcap_table", "pres_xcap")
 			in that request as the second parameter.
 	    	        </para>
 		        <para>
-			If HTTP server returns a class 2xx or 3xx reply,
+			If HTTP server returns a class 2xx, 3xx or 4xx reply,
 			the first line of the reply's body (if any) is
 			stored in <quote>result</quote> parameter,
 			which must be a	writable pseudo	variable.

+ 15 - 7
modules/utils/functions.c

@@ -139,16 +139,24 @@ int http_query(struct sip_msg* _m, char* _url, char* _dst, char* _post)
 	pkg_free(post);
     }
 
-    if (res != CURLE_OK) {
-	LM_ERR("failed to perform curl\n");
-	curl_easy_cleanup(curl);
-	if(stream)
-	    pkg_free(stream);
-	return -1;
+	if (res != CURLE_OK) {
+		/* http://curl.haxx.se/libcurl/c/libcurl-errors.html */
+		if (res == CURLE_COULDNT_CONNECT) {
+			LM_WARN("failed to connect() to host\n");
+		} else if ( res == CURLE_COULDNT_RESOLVE_HOST ) {
+			LM_WARN("couldn't resolve host\n");
+		} else {
+			LM_ERR("failed to perform curl (%d)\n", res);
+		}
+	
+		curl_easy_cleanup(curl);
+		if(stream)
+			pkg_free(stream);
+		return -1;
     }
 
     curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &stat);
-    if ((stat >= 200) && (stat < 400)) {
+    if ((stat >= 200) && (stat < 500)) {
 	curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &download_size);
 	LM_DBG("http_query download size: %u\n", (unsigned int)download_size);
 	/* search for line feed */

+ 161 - 19
modules/websocket/ws_conn.c

@@ -47,6 +47,9 @@ gen_lock_t *wsconn_lock = NULL;
 #define WSCONN_LOCK	lock_get(wsconn_lock)
 #define WSCONN_UNLOCK	lock_release(wsconn_lock)
 
+#define wsconn_ref(c)   atomic_inc(&((c)->refcnt))
+#define wsconn_unref(c) atomic_dec_and_test(&((c)->refcnt))
+
 gen_lock_t *wsstat_lock = NULL;
 
 ws_connection_used_list_t *wsconn_used_list = NULL;
@@ -197,6 +200,8 @@ int wsconn_add(struct receive_info rcv, unsigned int sub_protocol)
 	int id_hash = tcp_id_hash(id);
 	ws_connection_t *wsc;
 
+	LM_DBG("wsconn_add id [%d]\n", id);
+
 	/* Allocate and fill in new WebSocket connection */
 	wsc = shm_malloc(sizeof(ws_connection_t));
 	if (wsc == NULL)
@@ -210,6 +215,10 @@ int wsconn_add(struct receive_info rcv, unsigned int sub_protocol)
 	wsc->state = WS_S_OPEN;
 	wsc->rcv = rcv;
 	wsc->sub_protocol = sub_protocol;
+	wsc->run_event = 0;
+	atomic_set(&wsc->refcnt, 0);
+
+	LM_DBG("wsconn_add new wsc => [%p], ref => [%d]\n", wsc, atomic_get(&wsc->refcnt));
 
 	WSCONN_LOCK;
 	/* Add to WebSocket connection table */
@@ -225,8 +234,12 @@ int wsconn_add(struct receive_info rcv, unsigned int sub_protocol)
 		wsconn_used_list->tail->used_next = wsc;
 		wsconn_used_list->tail = wsc;
 	}
+	wsconn_ref(wsc);
+
 	WSCONN_UNLOCK;
 
+	LM_DBG("wsconn_add added to conn_table wsc => [%p], ref => [%d]\n", wsc, atomic_get(&wsc->refcnt));
+
 	/* Update connection statistics */
 	lock_get(wsstat_lock);
 
@@ -290,32 +303,29 @@ static void wsconn_run_route(ws_connection_t *wsc)
 	set_route_type(backup_rt);
 }
 
-int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route)
+static void wsconn_dtor(ws_connection_t *wsc)
 {
 	if (!wsc)
-	{
-		LM_ERR("wsconn_rm: null pointer\n");
-		return -1;
-	}
+		return;
 
-	if (run_event_route == WSCONN_EVENTROUTE_YES)
+	LM_DBG("wsconn_dtor for [%p] refcnt [%d]\n", wsc, atomic_get(&wsc->refcnt));
+
+	if (wsc->run_event)
 		wsconn_run_route(wsc);
 
-	WSCONN_LOCK;
-	/* Remove from the WebSocket used list */
-	if (wsconn_used_list->head == wsc)
-		wsconn_used_list->head = wsc->used_next;
-	if (wsconn_used_list->tail == wsc)
-		wsconn_used_list->tail = wsc->used_prev;
-	if (wsc->used_prev)
-		wsc->used_prev->used_next = wsc->used_next;
-	if (wsc->used_next)
-		wsc->used_next->used_prev = wsc->used_prev;
+	shm_free(wsc);
 
-	_wsconn_rm(wsc);
-	WSCONN_UNLOCK;
+	LM_DBG("wsconn_dtor for [%p] destroyed\n", wsc);
+}
 
-	return 0;
+int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route)
+{
+	LM_DBG("wsconn_rm for [%p] refcnt [%d]\n", wsc, atomic_get(&wsc->refcnt));
+
+	if (run_event_route == WSCONN_EVENTROUTE_YES)
+		wsc->run_event = 1;
+
+	return wsconn_put(wsc);
 }
 
 int wsconn_update(ws_connection_t *wsc)
@@ -366,17 +376,70 @@ void wsconn_close_now(ws_connection_t *wsc)
 	con->timeout = get_ticks_raw();
 }
 
+/* must be called with unlocked WSCONN_LOCK */
+int wsconn_put(ws_connection_t *wsc)
+{
+	int destroy = 0;
+
+	LM_DBG("wsconn_put start for [%p] refcnt [%d]\n", wsc, atomic_get(&wsc->refcnt));
+
+	if (!wsc)
+		return -1;
+
+	WSCONN_LOCK;
+	/* refcnt == 0*/
+	if (wsconn_unref(wsc))
+	{
+		/* Remove from the WebSocket used list */
+		if (wsconn_used_list->head == wsc)
+			wsconn_used_list->head = wsc->used_next;
+		if (wsconn_used_list->tail == wsc)
+			wsconn_used_list->tail = wsc->used_prev;
+		if (wsc->used_prev)
+			wsc->used_prev->used_next = wsc->used_next;
+		if (wsc->used_next)
+			wsc->used_next->used_prev = wsc->used_prev;
+
+		/* remove from wsconn_id_hash */
+		wsconn_listrm(wsconn_id_hash[wsc->id_hash], wsc, id_next, id_prev);
+
+		/* stat */
+		update_stat(ws_current_connections, -1);
+		if (wsc->sub_protocol == SUB_PROTOCOL_SIP)
+			update_stat(ws_sip_current_connections, -1);
+		else if (wsc->sub_protocol == SUB_PROTOCOL_MSRP)
+			update_stat(ws_msrp_current_connections, -1);
+
+		destroy = 1;
+	}
+	WSCONN_UNLOCK;
+
+	LM_DBG("wsconn_put end for [%p] refcnt [%d]\n", wsc, atomic_get(&wsc->refcnt));
+
+	/* wsc is removed from all lists and can be destroyed safely */
+	if (destroy)
+		wsconn_dtor(wsc);
+
+	return 0;
+}
+
 ws_connection_t *wsconn_get(int id)
 {
 	int id_hash = tcp_id_hash(id);
 	ws_connection_t *wsc;
 
+	LM_DBG("wsconn_get for id [%d]\n", id);
+
 	WSCONN_LOCK;
 	for (wsc = wsconn_id_hash[id_hash]; wsc; wsc = wsc->id_next)
 	{
 		if (wsc->id == id)
 		{
+			wsconn_ref(wsc);
+			LM_DBG("wsconn_get returns wsc [%p] refcnt [%d]\n", wsc, atomic_get(&wsc->refcnt));
+
 			WSCONN_UNLOCK;
+
 			return wsc;
 		}
 	}
@@ -385,6 +448,85 @@ ws_connection_t *wsconn_get(int id)
 	return NULL;
 }
 
+ws_connection_t **wsconn_get_list(void)
+{
+	ws_connection_t **list = NULL;
+	ws_connection_t *wsc   = NULL;
+	size_t list_size = 0;
+	size_t list_len  = 0;
+	size_t i = 0;
+
+	LM_DBG("wsconn_get_list\n");
+
+	WSCONN_LOCK;
+
+	/* get the number of used connections */
+	wsc = wsconn_used_list->head;
+	while (wsc)
+	{
+		LM_DBG("counter wsc [%p] prev => [%p] next => [%p]\n", wsc, wsc->used_prev, wsc->used_next);
+		list_len++;
+		wsc = wsc->used_next;
+	}
+
+	if (!list_len)
+		goto end;
+
+	/* allocate a NULL terminated list of wsconn pointers */
+	list_size = (list_len + 1) * sizeof(ws_connection_t *);
+	list = pkg_malloc(list_size);
+	if (!list)
+		goto end;
+
+	memset(list, 0, list_size);
+
+	/* copy */
+	wsc = wsconn_used_list->head;
+	for(i = 0; i < list_len; i++)
+	{
+		if (!wsc) {
+			LM_ERR("Wrong list length\n");
+		}
+
+		list[i] = wsc;
+		wsconn_ref(wsc);
+		LM_DBG("wsc [%p] id [%d] ref++\n", wsc, wsc->id);
+
+		wsc = wsc->used_next;
+	}
+	list[list_len] = NULL; /* explicit NULL termination */
+
+end:
+	WSCONN_UNLOCK;
+
+	LM_DBG("wsconn_get_list returns list [%p] with [%d] members\n", list, (int)list_len);
+
+	return list;
+}
+
+int wsconn_put_list(ws_connection_t **list_head)
+{
+	ws_connection_t **list = NULL;
+	ws_connection_t *wsc   = NULL;
+
+	LM_DBG("wsconn_put_list [%p]\n", list_head);
+
+	if (!list_head)
+		return -1;
+
+	list =  list_head;
+	wsc  = *list_head;
+	while (wsc)
+	{
+		wsconn_put(wsc);
+		wsc = *(++list);
+	}
+
+	pkg_free(list_head);
+
+	return 0;
+}
+
 static int add_node(struct mi_root *tree, ws_connection_t *wsc)
 {
 	int interval;

+ 8 - 0
modules/websocket/ws_conn.h

@@ -29,6 +29,8 @@
 #ifndef _WS_CONN_H
 #define _WS_CONN_H
 
+#include "../../atomic_ops.h"
+
 #include "../../lib/kcore/kstats_wrapper.h"
 #include "../../lib/kmi/tree.h"
 
@@ -57,6 +59,9 @@ typedef struct ws_connection
 	struct receive_info rcv;
 
 	unsigned int sub_protocol;
+
+	atomic_t refcnt;
+	int      run_event;
 } ws_connection_t;
 
 typedef struct
@@ -89,6 +94,9 @@ int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route);
 int wsconn_update(ws_connection_t *wsc);
 void wsconn_close_now(ws_connection_t *wsc);
 ws_connection_t *wsconn_get(int id);
+int wsconn_put(ws_connection_t *wsc);
+ws_connection_t **wsconn_get_list(void);
+int wsconn_put_list(ws_connection_t **list);
 struct mi_root *ws_mi_dump(struct mi_root *cmd, void *param);
 
 #endif /* _WS_CONN_H */

+ 122 - 58
modules/websocket/ws_frame.c

@@ -240,7 +240,6 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
 		pkg_free(send_buf);
 		if (wsconn_rm(frame->wsc, WSCONN_EVENTROUTE_YES) < 0)
 			LM_ERR("removing WebSocket connection\n");
-		frame->wsc = NULL;
 		return -1;
 	}
 	init_dst_from_rcv(&dst, &con->rcv);
@@ -252,10 +251,8 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
 			LM_ERR("removing WebSocket connection\n");
 			tcpconn_put(con);
 			pkg_free(send_buf);
-			frame->wsc = NULL;
 			return -1;
 		}
-		frame->wsc = NULL;
 	}
 
 	if (dst.proto == PROTO_WS)
@@ -308,7 +305,6 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
 			update_stat(ws_msrp_failed_connections, 1);
 		if (wsconn_rm(frame->wsc, WSCONN_EVENTROUTE_YES) < 0)
 			LM_ERR("removing WebSocket connection\n");
-		frame->wsc = NULL;
 		tcpconn_put(con);
 		return -1;
 	}
@@ -394,20 +390,19 @@ static int close_connection(ws_connection_t **p_wsc, ws_close_type_t type,
 			else if (sub_proto == SUB_PROTOCOL_MSRP)
 				update_stat(ws_msrp_remote_closed_connections,
 						1);
-			*p_wsc = NULL;
 		}
 	}
 	else /* if (frame->wsc->state == WS_S_CLOSING) */
 	{
 		wsconn_close_now(wsc);
-		*p_wsc = NULL;
 	}
 
 	return 0;
 }
 
 static int decode_and_validate_ws_frame(ws_frame_t *frame,
-					tcp_event_info_t *tcpinfo)
+                                        tcp_event_info_t *tcpinfo,
+                                        short *err_code, str *err_text)
 {
 	unsigned int i, len = tcpinfo->len;
 	int mask_start, j;
@@ -415,21 +410,14 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame,
 
 	LM_DBG("decoding WebSocket frame\n");
 
-	if ((frame->wsc = wsconn_get(tcpinfo->con->id)) == NULL)
-	{
-		LM_ERR("WebSocket connection not found\n");
-		return -1;
-	}
-
 	wsconn_update(frame->wsc);
 
 	/* Decode and validate first 9 bits */
 	if (len < 2)
 	{
 		LM_WARN("message is too short\n");
-		if (close_connection(&frame->wsc, LOCAL_CLOSE, 1002,
-					str_status_protocol_error) < 0)
-			LM_ERR("closing connection\n");
+		*err_code = 1002;
+		*err_text = str_status_protocol_error;
 		return -1;
 	}
 	frame->fin = (buf[0] & 0xff) & BYTE0_MASK_FIN;
@@ -443,18 +431,16 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame,
 	{
 		LM_WARN("WebSocket fragmentation not supported in the sip "
 			"sub-protocol\n");
-		if (close_connection(&frame->wsc, LOCAL_CLOSE, 1002,
-					str_status_protocol_error) < 0)
-			LM_ERR("closing connection\n");
+		*err_code = 1002;
+		*err_text = str_status_protocol_error;
 		return -1;
 	}
 
 	if (frame->rsv1 || frame->rsv2 || frame->rsv3)
 	{
 		LM_WARN("WebSocket reserved fields with non-zero values\n");
-		if (close_connection(&frame->wsc, LOCAL_CLOSE, 1002,
-					str_status_protocol_error) < 0)
-			LM_ERR("closing connection\n");
+		*err_code = 1002;
+		*err_text = str_status_protocol_error;
 		return -1;
 	}
 
@@ -476,9 +462,8 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame,
 	default:
 		LM_WARN("unsupported opcode: 0x%x\n",
 			(unsigned char) frame->opcode);
-		if (close_connection(&frame->wsc, LOCAL_CLOSE, 1008,
-					str_status_unsupported_opcode) < 0)
-			LM_ERR("closing connection\n");
+		*err_code = 1008;
+		*err_text = str_status_unsupported_opcode;
 		return -1;
 	}
 
@@ -486,9 +471,8 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame,
 	{
 		LM_WARN("this is a server - all received messages must be "
 			"masked\n");
-		if (close_connection(&frame->wsc, LOCAL_CLOSE, 1002,
-					str_status_protocol_error) < 0)
-			LM_ERR("closing connection\n");
+		*err_code = 1002;
+		*err_text = str_status_protocol_error;
 		return -1;
 	}
 
@@ -499,9 +483,8 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame,
 		if (len < 4)
 		{
 			LM_WARN("message is too short\n");
-			if (close_connection(&frame->wsc, LOCAL_CLOSE, 1002,
-						str_status_protocol_error) < 0)
-				LM_ERR("closing connection\n");
+			*err_code = 1002;
+			*err_text = str_status_protocol_error;
 			return -1;
 		}
 		mask_start = 4;
@@ -514,9 +497,8 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame,
 		if (len < 10)
 		{
 			LM_WARN("message is too short\n");
-			if (close_connection(&frame->wsc, LOCAL_CLOSE, 1002,
-						str_status_protocol_error) < 0)
-				LM_ERR("closing connection\n");
+			*err_code = 1002;
+			*err_text = str_status_protocol_error;
 			return -1;
 		}
 		mask_start = 10;
@@ -525,9 +507,8 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame,
 			|| (buf[4] & 0xff) != 0 || (buf[5] & 0xff) != 0)
 		{
 			LM_WARN("message is too long\n");
-			if (close_connection(&frame->wsc, LOCAL_CLOSE, 1009,
-						str_status_message_too_big) < 0)
-				LM_ERR("closing connection\n");
+			*err_code = 1009;
+			*err_text = str_status_message_too_big;
 			return -1;
 		}
 
@@ -553,9 +534,8 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame,
 	{
 		LM_WARN("message not complete frame size %u but received %u\n",
 			frame->payload_len + mask_start + 4, len);
-		if (close_connection(&frame->wsc, LOCAL_CLOSE, 1002,
-					str_status_protocol_error) < 0)
-			LM_ERR("closing connection\n");
+		*err_code = 1002;
+		*err_text = str_status_protocol_error;
 		return -1;
 	}
 	frame->payload_data = &buf[mask_start + 4];
@@ -632,6 +612,11 @@ int ws_frame_receive(void *data)
 	ws_frame_t frame;
 	tcp_event_info_t *tcpinfo = (tcp_event_info_t *) data;
 
+	int opcode      = -1;
+	int ret         = 0;
+	short err_code  = 0;
+	str   err_text  = {NULL, 0};
+
 	update_stat(ws_received_frames, 1);
 
 	if (tcpinfo == NULL || tcpinfo->buf == NULL || tcpinfo->len <= 0)
@@ -640,7 +625,26 @@ int ws_frame_receive(void *data)
 		return -1;
 	}
 
-	switch(decode_and_validate_ws_frame(&frame, tcpinfo))
+	/* wsc refcnt++ */
+	frame.wsc = wsconn_get(tcpinfo->con->id);
+	if (frame.wsc == NULL)
+	{
+		LM_ERR("WebSocket connection not found\n");
+		return -1;
+	}
+
+	opcode = decode_and_validate_ws_frame(&frame, tcpinfo, &err_code, &err_text);
+	if (opcode < 0)
+	{
+		if (close_connection(&frame.wsc, LOCAL_CLOSE, err_code, err_text) < 0)
+			LM_ERR("closing connection\n");
+
+		wsconn_put(frame.wsc);
+
+		return -1;
+	}
+
+	switch(opcode)
 	{
 	case OPCODE_TEXT_FRAME:
 	case OPCODE_BINARY_FRAME:
@@ -649,6 +653,9 @@ int ws_frame_receive(void *data)
 			LM_DBG("Rx SIP message:\n%.*s\n", frame.payload_len,
 				frame.payload_data);
 			update_stat(ws_sip_received_frames, 1);
+
+			wsconn_put(frame.wsc);
+
 			return receive_msg(frame.payload_data,
 						frame.payload_len,
 						tcpinfo->rcv);
@@ -667,30 +674,46 @@ int ws_frame_receive(void *data)
 				tev.len = frame.payload_len;
 				tev.rcv = tcpinfo->rcv;
 				tev.con = tcpinfo->con;
+
+				wsconn_put(frame.wsc);
+
 				return sr_event_exec(SREV_TCP_MSRP_FRAME,
 							(void *) &tev);
 			}
 			else
 			{
 				LM_ERR("no callback registered for MSRP\n");
+
+				wsconn_put(frame.wsc);
+
 				return -1;
 			}
 		}
 
 	case OPCODE_CLOSE:
-		return handle_close(&frame);
+		ret = handle_close(&frame);
+		if (frame.wsc) wsconn_put(frame.wsc);
+		return ret;
 
 	case OPCODE_PING:
-		return handle_ping(&frame);
+		ret = handle_ping(&frame);
+		if (frame.wsc) wsconn_put(frame.wsc);
+		return ret;
 
 	case OPCODE_PONG:
-		return handle_pong(&frame);
+		ret = handle_pong(&frame);
+		if (frame.wsc) wsconn_put(frame.wsc);
+		return ret;
 
 	default:
 		LM_WARN("received bad frame\n");
+		wsconn_put(frame.wsc);
 		return -1;
 	}
 
+	/* how can we get here ? */
+	wsconn_put(frame.wsc);
+
 	return 0;
 }
 
@@ -715,9 +738,14 @@ int ws_frame_transmit(void *data)
 	if (encode_and_send_ws_frame(&frame, CONN_CLOSE_DONT) < 0)
 	{	
 		LM_ERR("sending message\n");
+
+		wsconn_put(frame.wsc);
+
 		return -1;
 	}
 
+	wsconn_put(frame.wsc);
+
 	return 0;
 }
 
@@ -783,8 +811,11 @@ struct mi_root *ws_mi_close(struct mi_root *cmd, void *param)
 					str_status_bad_param.len);
 	}
 
-	if (close_connection(&wsc, LOCAL_CLOSE, 1000,
-				str_status_normal_closure) < 0)
+	int ret = close_connection(&wsc, LOCAL_CLOSE, 1000, str_status_normal_closure);
+
+	wsconn_put(wsc);
+
+	if (ret < 0)
 	{
 		LM_WARN("closing connection\n");
 		return init_mi_tree(500, str_status_error_closing.s,
@@ -834,7 +865,11 @@ static struct mi_root *mi_ping_pong(struct mi_root *cmd, void *param,
 					str_status_bad_param.len);
 	}
 
-	if (ping_pong(wsc, opcode) < 0)
+	int ret = ping_pong(wsc, opcode);
+
+	wsconn_put(wsc);
+
+	if (ret < 0)
 	{
 		LM_WARN("sending %s\n", OPCODE_PING ? "Ping" : "Pong");
 		return init_mi_tree(500, str_status_error_sending.s,
@@ -858,36 +893,55 @@ void ws_keepalive(unsigned int ticks, void *param)
 {
 	int check_time = (int) time(NULL)
 		- cfg_get(websocket, ws_cfg, keepalive_timeout);
-	ws_connection_t *wsc = wsconn_used_list->head;
 
+	ws_connection_t **list      = NULL,
+	                **list_head = NULL;
+	ws_connection_t *wsc   = NULL;
+
+	/* get an array of pointer to all ws connection */
+	list_head = wsconn_get_list();
+	if (!list_head)
+		return;
+
+	list =  list_head;
+	wsc  = *list_head;
 	while (wsc && wsc->last_used < check_time)
 	{
-		if (wsc->state == WS_S_CLOSING
-			|| wsc->awaiting_pong)
+		if (wsc->state == WS_S_CLOSING || wsc->awaiting_pong)
 		{
 			LM_WARN("forcibly closing connection\n");
 			wsconn_close_now(wsc);
 		}
 		else
-			ping_pong(wsconn_used_list->head,
-			  ws_keepalive_mechanism == KEEPALIVE_MECHANISM_PING
-					? OPCODE_PING : OPCODE_PONG);
-		wsc = wsconn_used_list->head;
+		{
+			int opcode = (ws_keepalive_mechanism == KEEPALIVE_MECHANISM_PING)
+			             ? OPCODE_PING
+			             : OPCODE_PONG;
+			ping_pong(wsc, opcode);
+		}
+
+		wsc = *(++list);
 	}
-	
+
+	wsconn_put_list(list_head);
 }
 
 int ws_close(sip_msg_t *msg)
 {
 	ws_connection_t *wsc;
+	int ret;
 
 	if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) == NULL) {
 		LM_ERR("failed to retrieve WebSocket connection\n");
 		return -1;
 	}
 
-	return (close_connection(&wsc, LOCAL_CLOSE, 1000,
+	ret = (close_connection(&wsc, LOCAL_CLOSE, 1000,
 				 str_status_normal_closure) == 0) ? 1: 0;
+
+	wsconn_put(wsc);
+
+	return ret;
 }
 
 int ws_close2(sip_msg_t *msg, char *_status, char *_reason)
@@ -895,6 +949,7 @@ int ws_close2(sip_msg_t *msg, char *_status, char *_reason)
 	int status;
 	str reason;
 	ws_connection_t *wsc;
+	int ret;
 
 	if (get_int_fparam(&status, msg, (fparam_t *) _status) < 0) {
 		LM_ERR("failed to get status code\n");
@@ -911,7 +966,11 @@ int ws_close2(sip_msg_t *msg, char *_status, char *_reason)
 		return -1;
 	}
 
-	return (close_connection(&wsc, LOCAL_CLOSE, status, reason) == 0) ? 1: 0;
+	ret = (close_connection(&wsc, LOCAL_CLOSE, status, reason) == 0) ? 1: 0;
+
+	wsconn_put(wsc);
+
+	return ret;
 }
 
 int ws_close3(sip_msg_t *msg, char *_status, char *_reason, char *_con)
@@ -920,6 +979,7 @@ int ws_close3(sip_msg_t *msg, char *_status, char *_reason, char *_con)
 	str reason;
 	int con;
 	ws_connection_t *wsc;
+	int ret;
 
 	if (get_int_fparam(&status, msg, (fparam_t *) _status) < 0) {
 		LM_ERR("failed to get status code\n");
@@ -941,5 +1001,9 @@ int ws_close3(sip_msg_t *msg, char *_status, char *_reason, char *_con)
 		return -1;
 	}
 
-	return (close_connection(&wsc, LOCAL_CLOSE, status, reason) == 0) ? 1: 0;
+	ret = (close_connection(&wsc, LOCAL_CLOSE, status, reason) == 0) ? 1: 0;
+
+	wsconn_put(wsc);
+
+	return ret;
 }

+ 3 - 1
modules/websocket/ws_handshake.c

@@ -427,8 +427,10 @@ int ws_handle_handshake(struct sip_msg *msg)
 				&headers) < 0)
 	{
 		if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL)
+		{
 			wsconn_rm(wsc, WSCONN_EVENTROUTE_NO);
-
+			wsconn_put(wsc);
+		}
 		goto end;
 	}
 	else

+ 299 - 3
msg_translator.c

@@ -147,8 +147,9 @@
 #include "pt.h"
 #include "cfg/cfg.h"
 #include "parser/parse_to.h"
+#include "parser/parse_param.h"
 #include "forward.h"
-
+#include "str_list.h"
 
 #define append_str_trans(_dest,_src,_len,_msg) \
 	append_str( (_dest), (_src), (_len) );
@@ -1620,7 +1621,301 @@ error:
 	return -1;
 }
 
+static inline int find_line_start(char *text, unsigned int text_len,
+				  char **buf, unsigned int *buf_len)
+{
+	char *ch, *start;
+	unsigned int len;
+
+	start = *buf;
+	len = *buf_len;
+
+	while (text_len <= len) {
+		if (strncmp(text, start, text_len) == 0) {
+			*buf = start;
+			*buf_len = len;
+			return 1;
+		}
+		if ((ch = memchr(start, 13, len - 1))) {
+			if (*(ch + 1) != 10) {
+				LM_ERR("No LF after CR\n");
+				return 0;
+			}
+			len = len - (ch - start + 2);
+			start = ch + 2;
+		} else {
+			LM_ERR("No CRLF found\n");
+			return 0;
+		}
+	}
+	return 0;
+}
+
+static inline int get_line(str s)
+{
+	char *ch;
+
+	if ((ch = memchr(s.s, 13, s.len))) {
+		if (*(ch + 1) != 10) {
+			LM_ERR("No LF after CR\n");
+			return 0;
+		}
+		return ch - s.s + 2;
+	} else {
+		LM_ERR("No CRLF found\n");
+		return s.len;
+	}
+	return 0;
+}
+
+int replace_body(struct sip_msg *msg, str txt)
+{
+	struct lump *anchor;
+	char *buf;
+	str body = {0,0};
+
+	body.s = get_body(msg);
+	if(body.s==0)
+	{
+		LM_ERR("malformed sip message\n");
+		return 0;
+	}
+	body.len = msg->len -(int)(body.s-msg->buf);
+	LM_DBG("old size body[%d] actual[%d]\n", body.len, txt.len);
+	if(body.s+body.len>msg->buf+msg->len)
+	{
+		LM_ERR("invalid content length: %d\n", body.len);
+		return 0;
+	}
+	del_nonshm_lump( &(msg->body_lumps) );
+	msg->body_lumps = NULL;
+
+	if(del_lump(msg, body.s-msg->buf, body.len, 0) == 0)
+	{
+		LM_ERR("cannot delete existing body");
+		return 0;
+	}
+
+	anchor = anchor_lump(msg, body.s - msg->buf, 0, 0);
+	if(anchor==0)
+	{
+		LM_ERR("failed to get anchor\n");
+		return 0;
+	}
+
+	buf=pkg_malloc(sizeof(char)*txt.len);
+	if(buf==0)
+	{
+		PKG_MEM_ERROR;
+		return 0;
+	}
+	memcpy(buf, txt.s, txt.len);
+	if(insert_new_lump_after(anchor, buf, txt.len, 0)==0)
+	{
+		LM_ERR("failed to insert body lump\n");
+		pkg_free(buf);
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * returns the boundary defined by the Content-Type
+ * header
+ */
+int get_boundary(struct sip_msg* msg, str* boundary)
+{
+	str params;
+	param_t *p, *list;
+	param_hooks_t hooks;
+
+	params.s = memchr(msg->content_type->body.s, ';',
+		msg->content_type->body.len);
+	if (params.s == NULL)
+	{
+		LM_ERR("Content-Type hdr has no params\n");
+		return -1;
+	}
+	params.len = msg->content_type->body.len -
+		(params.s - msg->content_type->body.s);
+	if (parse_params(&params, CLASS_ANY, &hooks, &list) < 0)
+	{
+		LM_ERR("while parsing Content-Type params\n");
+		return -1;
+	}
+	boundary->s = NULL;
+	boundary->len = 0;
+	for (p = list; p; p = p->next) {
+		if ((p->name.len == 8)
+			&& (strncasecmp(p->name.s, "boundary", 8) == 0))
+		{
+			boundary->s = pkg_malloc(p->body.len + 2);
+			if (boundary->s == NULL)
+			{
+				free_params(list);
+				LM_ERR("no memory for boundary string\n");
+				return -1;
+			}
+			*(boundary->s) = '-';
+			*(boundary->s + 1) = '-';
+			memcpy(boundary->s + 2, p->body.s, p->body.len);
+			boundary->len = 2 + p->body.len;
+			LM_DBG("boundary is <%.*s>\n", boundary->len, boundary->s);
+			break;
+		}
+	}
+	free_params(list);
+	return 0;
+}
+
+int check_boundaries(struct sip_msg *msg, struct dest_info *send_info)
+{
+	str b = {0,0};
+	str fb = {0,0};
+	str ob = {0,0};
+	str bsuffix = {"\r\n", 2};
+	str fsuffix = {"--\r\n", 4};
+	str body = {0,0};
+	str buf = {0,0};
+	str tmp = {0,0};
+	struct str_list* lb = NULL;
+	struct str_list* lb_t = NULL;
+	int lb_found = 0;
+	int t, ret, lb_size;
+	char *pb;
+
+	if(!(msg->msg_flags&FL_BODY_MULTIPART)) return 0;
+	else
+	{
+		buf.s = build_body(msg, (unsigned int *)&buf.len, &ret, send_info);
+		if(ret) {
+			LM_ERR("Can't get body\n");
+			return -1;
+		}
+		tmp.s = buf.s;
+		t = tmp.len = buf.len;
+		if(get_boundary(msg, &ob)!=0) return -1;
+		if(str_append(&ob, &bsuffix, &b)!=0) {
+			LM_ERR("Can't append suffix to boundary\n");
+			goto error;
+		}
+		if(str_append(&ob, &fsuffix,&fb)!=0) {
+			LM_ERR("Can't append suffix to final boundary\n");
+			goto error;
+		}
+		ret = b.len-2;
+		while(t>0)
+		{
+			if(find_line_start(b.s, ret, &tmp.s,
+				(unsigned int *)&tmp.len))
+			{
+				/*LM_DBG("found t[%d] tmp.len[%d]:[%.*s]\n",
+					t, tmp.len, tmp.len, tmp.s);*/
+				if(!lb)
+				{
+					lb = pkg_malloc(sizeof(struct str_list));
+					if (!lb) {
+						PKG_MEM_ERROR;
+						goto error;
+					}
+					lb->s.s = tmp.s;
+					lb->s.len = tmp.len;
+					lb->next = 0;
+					lb_t = lb;
+				}
+				else
+				{
+					lb_t = append_str_list(tmp.s, tmp.len, &lb_t, &lb_size);
+				}
+				lb_found = lb_found + 1;
+				tmp.s = tmp.s + ret;
+				t =  t - ret;
+				tmp.len = tmp.len - ret;
+			}
+			else { t=0; }
+		}
+		if(lb_found<2)
+		{
+			LM_ERR("found[%d] wrong number of boundaries\n", lb_found);
+			goto error;
+		}
+		/* adding 2 chars in advance */
+		body.len = buf.len + 2;
+		body.s = pkg_malloc(sizeof(char)*body.len);
+		if (!body.s) {
+			PKG_MEM_ERROR;
+			goto error;
+		}
+		pb = body.s; body.len = 0;
+		lb_t = lb;
+		while(lb_t)
+		{
+			tmp.s = lb_t->s.s; tmp.len = lb_t->s.len;
+			tmp.len = get_line(lb_t->s);
+			if(tmp.len!=b.len || strncmp(b.s, tmp.s, b.len)!=0)
+			{
+				LM_DBG("malformed bondary in the middle\n");
+				memcpy(pb, b.s, b.len); body.len = body.len + b.len;
+				pb = pb + b.len;
+				t = lb_t->s.s - (lb_t->s.s + tmp.len);
+				memcpy(pb, lb_t->s.s+tmp.len, t); pb = pb + t;
+				/*LM_DBG("new chunk[%d][%.*s]\n", t, t, pb-t);*/
+			}
+			else {
+				t = lb_t->next->s.s - lb_t->s.s;
+				memcpy(pb, lb_t->s.s, t);
+				/*LM_DBG("copy[%d][%.*s]\n", t, t, pb);*/
+				pb = pb + t;
+			}
+			body.len = body.len + t;
+			/*LM_DBG("body[%d][%.*s]\n", body.len, body.len, body.s);*/
+			lb_t = lb_t->next;
+			if(!lb_t->next) lb_t = NULL;
+		}
+		/* last boundary */
+		tmp.s = lb->s.s; tmp.len = lb->s.len;
+		tmp.len = get_line(lb->s);
+		if(tmp.len!=fb.len || strncmp(fb.s, tmp.s, fb.len)!=0)
+		{
+			LM_DBG("last bondary without -- at the end\n");
+			memcpy(pb, fb.s, fb.len);
+			/*LM_DBG("new chunk[%d][%.*s]\n", fb.len, fb.len, pb);*/
+			pb = pb + fb.len;
+			body.len = body.len + fb.len;
+		}
+		else {
+			memcpy(pb, lb->s.s, lb->s.len); pb = pb + lb->s.len;
+			body.len = body.len + lb->s.len;
+			/*LM_DBG("copy[%d][%.*s]\n", lb->s.len, lb->s.len, pb - lb->s.len);*/
+		}
+		/*LM_DBG("body[%d][%.*s] expected[%ld]\n",
+			body.len, body.len, body.s, pb-body.s); */
+		if(!replace_body(msg, body))
+		{
+			LM_ERR("Can't replace body\n");
+			goto error;
+		}
+		msg->msg_flags &= ~FL_BODY_MULTIPART;
+		ret = 1;
+		goto clean;
+	}
 
+error:
+	ret = -1;
+clean:
+	if(ob.s) pkg_free(ob.s);
+	if(b.s) pkg_free(b.s);
+	if(fb.s) pkg_free(fb.s);
+	if(body.s) pkg_free(body.s);
+	if(buf.s) pkg_free(buf.s);
+	while(lb)
+	{
+		lb_t = lb->next;
+		pkg_free(lb);
+		lb = lb_t;
+	}
+	return ret;
+}
 
 /** builds a request in memory from another sip request.
   *
@@ -1700,6 +1995,9 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 	path_buf.len=0;
 
 	flags=msg->msg_flags|global_req_flags;
+	if(check_boundaries(msg, send_info)<0){
+		LM_WARN("check_boundaries error\n");
+	}
 	/* Calculate message body difference and adjust Content-Length */
 	body_delta = lumps_len(msg, msg->body_lumps, send_info);
 	if (adjust_clen(msg, body_delta, send_info->proto) < 0) {
@@ -1956,8 +2254,6 @@ error00:
 	return 0;
 }
 
-
-
 char * generate_res_buf_from_sip_res( struct sip_msg* msg,
 				unsigned int *returned_len, unsigned int mode)
 {

+ 3 - 0
msg_translator.h

@@ -162,4 +162,7 @@ void fix_global_req_flags(str* gname, str* name);
 
 int build_sip_msg_from_buf(struct sip_msg *msg, char *buf, int len,
 		unsigned int id);
+
+/* returns a copy in private memory of the boundary in a multipart body */
+int get_boundary(struct sip_msg* msg, str* boundary);
 #endif

+ 7 - 5
onsend.h

@@ -43,10 +43,11 @@
 #include "sr_compat.h"
 
 struct onsend_info{
-	union sockaddr_union* to;
-	struct socket_info* send_sock;
-	char* buf;
-	int len;
+	union sockaddr_union* to;       /* dest info */
+	struct socket_info* send_sock;  /* local send socket */
+	char* buf;                      /* outgoing buffer */
+	int len;                        /* outgoing buffer len */
+	sip_msg_t *msg;                 /* original sip msg struct */
 };
 
 extern struct onsend_info* p_onsend;
@@ -61,7 +62,7 @@ extern struct onsend_info* p_onsend;
 static inline int run_onsend(struct sip_msg* orig_msg, struct dest_info* dst,
 								char* buf, int len)
 {
-	struct onsend_info onsnd_info;
+	struct onsend_info onsnd_info = {0};
 	int ret;
 	struct run_act_ctx ra_ctx;
 	int backup_route_type;
@@ -74,6 +75,7 @@ static inline int run_onsend(struct sip_msg* orig_msg, struct dest_info* dst,
 		onsnd_info.send_sock=dst->send_sock;
 		onsnd_info.buf=buf;
 		onsnd_info.len=len;
+		onsnd_info.msg=orig_msg;
 		p_onsend=&onsnd_info;
 		backup_route_type=get_route_type();
 		set_route_type(ONSEND_ROUTE);

+ 2 - 1
parser/sdp/sdp.c

@@ -871,12 +871,13 @@ void print_sdp_stream(sdp_stream_cell_t *stream, int log_level)
 
 void print_sdp_session(sdp_session_cell_t *session, int log_level)
 {
-	sdp_stream_cell_t *stream = session->streams;
+	sdp_stream_cell_t *stream;
 
 	if (session==NULL) {
 		LM_ERR("NULL session\n");
 		return;
 	}
+	stream = session->streams;
 
 	LOG(log_level, "..session[%d]:%p=>%p '%.*s' '%.*s' '%.*s' '%.*s:%.*s' (%d)=>%p\n",
 		session->session_num, session, session->next,

+ 1 - 0
pkg/kamailio/deb/debian/control

@@ -40,6 +40,7 @@ Build-Depends: bison,
                libxml2-dev,
                libxmlrpc-c3-dev,
                openssl,
+               pkg-config,
                python,
                python-dev,
                unixodbc-dev,

+ 1 - 0
pkg/kamailio/deb/jessie/control

@@ -39,6 +39,7 @@ Build-Depends: bison,
                libval-dev,
                libxml2-dev,
                openssl,
+               pkg-config,
                python,
                python-dev,
                unixodbc-dev,

+ 1 - 0
pkg/kamailio/deb/precise/control

@@ -38,6 +38,7 @@ Build-Depends: bison,
                libunistring-dev,
                libxml2-dev,
                openssl,
+               pkg-config,
                python,
                python-dev,
                unixodbc-dev,

+ 1 - 0
pkg/kamailio/deb/squeeze/control

@@ -33,6 +33,7 @@ Build-Depends: bison,
                libxml2-dev,
                libxmlrpc-c3-dev,
                openssl,
+               pkg-config,
                python,
                python-dev,
                unixodbc-dev,

+ 1 - 0
pkg/kamailio/deb/wheezy/control

@@ -37,6 +37,7 @@ Build-Depends: bison,
                libunistring-dev,
                libxml2-dev,
                openssl,
+               pkg-config,
                python,
                python-dev,
                unixodbc-dev,

+ 1 - 1
pkg/kamailio/rpm/kamailio.init

@@ -35,7 +35,7 @@ check_fork ()
 check_kamailio_config ()
 {
         # Check if kamailio configuration is valid before starting the server
-        out=$($KAM -c 2>&1 > /dev/null)
+        out=$($KAM -M $PKG_MEMORY -c 2>&1 > /dev/null)
         retcode=$?
         if [ "$retcode" != '0' ]; then
             echo "Not starting $DESC: invalid configuration file!"

+ 4 - 8
route.c

@@ -1886,8 +1886,7 @@ inline static int eval_elem(struct run_act_ctx* h, struct expr* e,
 			if (e->r_type==MYSELF_ST){
 				if (parse_sip_msg_uri(msg)<0) ret=-1;
 				else ret=check_self_op(e->op, &msg->parsed_uri.host,
-						       msg->parsed_uri.port_no?
-						       msg->parsed_uri.port_no:SIP_PORT);
+								GET_URI_PORT(&msg->parsed_uri));
 			}else{
 				ret=comp_str(e->op, &msg->new_uri,
 								e->r_type, &e->r, msg, h);
@@ -1896,8 +1895,7 @@ inline static int eval_elem(struct run_act_ctx* h, struct expr* e,
 			if (e->r_type==MYSELF_ST){
 				if (parse_sip_msg_uri(msg)<0) ret=-1;
 				else ret=check_self_op(e->op, &msg->parsed_uri.host,
-						       msg->parsed_uri.port_no?
-						       msg->parsed_uri.port_no:SIP_PORT);
+								GET_URI_PORT(&msg->parsed_uri));
 			}else{
 				ret=comp_str(e->op, &msg->first_line.u.request.uri,
 								e->r_type, &e->r, msg, h);
@@ -1917,8 +1915,7 @@ inline static int eval_elem(struct run_act_ctx* h, struct expr* e,
 				LOG(L_ERR, "ERROR: eval_elem: bad uri in From:\n");
 				goto error;
 			}
-			ret=check_self_op(e->op, &uri.host,
-					  uri.port_no?uri.port_no:SIP_PORT);
+			ret=check_self_op(e->op, &uri.host, GET_URI_PORT(&uri));
 		}else{
 			ret=comp_str(e->op, &get_from(msg)->uri,
 							e->r_type, &e->r, msg, h);
@@ -1939,8 +1936,7 @@ inline static int eval_elem(struct run_act_ctx* h, struct expr* e,
 				LOG(L_ERR, "ERROR: eval_elem: bad uri in To:\n");
 				goto error;
 			}
-			ret=check_self_op(e->op, &uri.host,
-					  uri.port_no?uri.port_no:SIP_PORT);
+			ret=check_self_op(e->op, &uri.host, GET_URI_PORT(&uri));
 		}else{
 			ret=comp_str(e->op, &get_to(msg)->uri,
 							e->r_type, &e->r, msg, h);

+ 1 - 0
rvalue.c

@@ -2554,6 +2554,7 @@ struct rval_expr* mk_rval_expr_v(enum rval_type rv_type, void* val,
 			s=(str*)val;
 			v.s.s=pkg_malloc(s->len+1 /*0*/);
 			if (v.s.s==0){
+				pkg_free(rve);
 				ERR("memory allocation failure\n");
 				return 0;
 			}

+ 94 - 0
sr_module.c

@@ -1776,6 +1776,100 @@ int get_int_fparam(int* dst, struct sip_msg* msg, fparam_t* param)
 	return 0;
 }
 
+/** Get the function parameter value as string or/and integer (if possible).
+ *  @return  0 - Success
+ *          -1 - Cannot get value
+ */
+int get_is_fparam(int* i_dst, str* s_dst, struct sip_msg* msg, fparam_t* param, unsigned int *flags)
+{
+	int_str val;
+	int ret;
+	avp_t* avp;
+	str tmp;
+	pv_value_t pv_val;
+
+	*flags = 0;
+	switch(param->type) {
+		case FPARAM_INT:
+			*i_dst = param->v.i;
+			*flags |= PARAM_INT;
+			return 0;
+		case FPARAM_REGEX:
+		case FPARAM_UNSPEC:
+		case FPARAM_STRING:
+			s_dst->s = param->v.asciiz;
+			s_dst->len = strlen(param->v.asciiz);
+			*flags |= PARAM_STR;
+			break;
+		case FPARAM_STR:
+			*s_dst = param->v.str;
+			*flags |= PARAM_STR;
+			break;
+		case FPARAM_AVP:
+			avp = search_first_avp(param->v.avp.flags, param->v.avp.name,
+									&val, 0);
+			if (unlikely(!avp)) {
+				DBG("Could not find AVP from function parameter '%s'\n",
+						param->orig);
+				return -1;
+			}
+			if (avp->flags & AVP_VAL_STR) {
+				*s_dst = val.s;
+				*flags |= PARAM_STR;
+				if (str2int(&val.s, (unsigned int*)i_dst) < 0) {
+					ERR("Could not convert AVP string value to int\n");
+					return -1;
+				}
+			} else {
+				*i_dst = val.n;
+				*flags |= PARAM_INT;
+			}
+			break;
+		case FPARAM_SELECT:
+			ret = run_select(&tmp, param->v.select, msg);
+			if (unlikely(ret < 0 || ret > 0)) return -1;
+			if (unlikely(str2int(&tmp, (unsigned int*)i_dst) < 0)) {
+				ERR("Could not convert select result to int\n");
+				return -1;
+			}
+			*flags |= PARAM_INT;
+			break;
+		case FPARAM_PVS:
+			if (likely(pv_get_spec_value(msg, param->v.pvs, &pv_val)==0)) {
+				if ((pv_val.flags&(PV_VAL_NULL|PV_VAL_INT))==PV_VAL_INT){
+					*i_dst=pv_val.ri;
+					*flags |= PARAM_INT;
+				}
+				if ((pv_val.flags&(PV_VAL_NULL|PV_VAL_STR))==PV_VAL_STR){
+					*s_dst=pv_val.rs;
+					*flags |= PARAM_STR;
+				}
+			}else{
+				ERR("Could not get PV\n");
+				return -1;
+			}
+			break;
+		case FPARAM_PVE:
+			s_dst->s=pv_get_buffer();
+			s_dst->len=pv_get_buffer_size();
+			if (unlikely(pv_printf(msg, param->v.pve, s_dst->s, &s_dst->len)!=0)){
+				ERR("Could not convert the PV-formated string to str\n");
+				s_dst->len=0;
+				return -1;
+			}
+			*flags |= PARAM_STR;
+			break;
+	}
+
+	/* Let's convert to int, if possible */
+	if (!(*flags & PARAM_INT) && (*flags & PARAM_STR) && str2sint(s_dst, i_dst) == 0)
+		*flags |= PARAM_INT;
+
+	if (!*flags) return -1;
+
+	return 0;
+}
+
 /**
  * Retrieve the compiled RegExp.
  * @return: 0 for success, negative on error.

+ 11 - 0
sr_module.h

@@ -629,6 +629,17 @@ int get_str_fparam(str* dst, struct sip_msg* msg, fparam_t* param);
  */
 int get_int_fparam(int* dst, struct sip_msg* msg, fparam_t* param);
 
+/**
+ * @brief Get the function parameter value as integer/string
+ * @param i_dst int destination
+ * @param s_dst string destination
+ * @param msg SIP message
+ * @param param function parameters
+ * @param flags flags to indicate destiantions
+ * @return 0 on success, 1 on error, e.g. cannot get value
+ */
+int get_is_fparam(int* i_dst, str* s_dst, struct sip_msg* msg, fparam_t* param, unsigned int *flags);
+
 /**
  * @brief Get the function parameter value as compiled regular expression
  * @param dst string destination

+ 45 - 0
str.c

@@ -0,0 +1,45 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2014 Victor Seva <[email protected]>
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+#include "str.h"
+#include "mem/mem.h"
+
+int str_append(str *orig, str *suffix, str *dest)
+{
+	if(orig == NULL || suffix == NULL || suffix->len == 0 || dest == NULL)
+	{
+		LM_ERR("wrong parameters\n");
+		return -1;
+	}
+	dest->len = orig->len + suffix->len;
+	dest->s = pkg_malloc(sizeof(char)*dest->len);
+	if(dest->s==NULL)
+	{
+		LOG(L_ERR, "memory allocation failure\n");
+		return -1;
+	}
+	if(orig->len>0)
+	{
+		memcpy(dest->s, orig->s, orig->len);
+	}
+	memcpy(dest->s+orig->len, suffix->s, suffix->len);
+	return 0;
+}

+ 9 - 0
str.h

@@ -121,4 +121,13 @@ typedef struct _str str;
 
 /** @} */
 
+/** Appends a sufffix
+ * @param orig is the original string
+ * @param suffix is the suffix string
+ * @param dest is the result ::str of appending suffix to orig
+ * @return 0 if ok -1 if error
+ * remember to free the dest->s private memory
+ */
+int str_append(str *orig, str *suffix, str *dest);
+
 #endif

+ 11 - 0
tcp_main.c

@@ -4934,6 +4934,17 @@ int tcp_init_children()
 			}
 		}
 	}
+#ifdef USE_TLS
+	for(si=tls_listen; si; si=si->next) {
+		if(si->workers>0) {
+			si->workers_tcpidx = i - si->workers + 1;
+			for(r=0; r<si->workers; r++) {
+				tcp_children[i].mysocket = si;
+				i--;
+			}
+		}
+	}
+#endif
 	tcp_sockets_gworkers = (i != tcp_children_no-1)?(1 + i + 1):0;
 
 	/* create the tcp sock_info structures */

Некоторые файлы не были показаны из-за большого количества измененных файлов