2
0
Эх сурвалжийг харах

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

Forgot to pull before commit.
Juha Heinanen 13 жил өмнө
parent
commit
1d23563dd6
43 өөрчлөгдсөн 3125 нэмэгдсэн , 532 устгасан
  1. 3 1
      etc/kamailio.cfg
  2. 1 1
      lib/srdb1/db_op.h
  3. 1 1
      lib/srdb1/schema/silo.xml
  4. 0 1
      modules/auth_identity/auth_http.c
  5. 0 1
      modules/auth_identity/auth_identity.c
  6. 27 12
      modules/db_postgres/km_dbase.c
  7. 1 1
      modules_k/dialog/dlg_cb.h
  8. 5 4
      modules_k/dialog/dlg_hash.c
  9. 14 9
      modules_k/msilo/msilo.c
  10. 156 90
      modules_k/presence/README
  11. 84 1
      modules_k/presence/doc/presence_admin.xml
  12. 1029 25
      modules_k/presence/notify.c
  13. 4 0
      modules_k/presence/notify.h
  14. 92 24
      modules_k/presence/presence.c
  15. 7 0
      modules_k/presence/presence.h
  16. 229 17
      modules_k/presence/presentity.c
  17. 2 0
      modules_k/presence/presentity.h
  18. 51 19
      modules_k/presence/publish.c
  19. 182 84
      modules_k/presence/subscribe.c
  20. 1 5
      modules_k/presence/subscribe.h
  21. 44 0
      modules_k/pua/pua_db.c
  22. 4 1
      modules_k/pua_reginfo/usrloc_cb.c
  23. 12 4
      modules_k/purple/Makefile
  24. 1 0
      modules_k/purple/purple.h
  25. 1 0
      modules_k/purple/purplepipe.h
  26. 1 1
      modules_k/purple/utils.h
  27. 0 9
      modules_k/rls/README
  28. 2 13
      modules_k/rls/doc/rls_admin.xml
  29. 36 32
      modules_k/rls/notify.c
  30. 51 47
      modules_k/rls/resource_notify.c
  31. 12 36
      modules_k/rls/rls_db.c
  32. 6 0
      modules_k/rls/subscribe.c
  33. 1 0
      modules_k/usrloc/Makefile
  34. 9 1
      modules_k/usrloc/ul_mi.c
  35. 7 0
      modules_k/usrloc/ul_mod.c
  36. 0 75
      pkg/kamailio/fedora/kamailio.init
  37. 12 0
      pkg/kamailio/fedora/kamailio.service
  38. 881 0
      pkg/kamailio/fedora/kamailio.spec
  39. 14 0
      pkg/kamailio/fedora/kamailio.sysconfig
  40. 91 9
      utils/kamctl/kamctl
  41. 22 6
      utils/kamctl/kamctl.base
  42. 26 0
      utils/kamctl/kamctl.ctlbase
  43. 3 2
      utils/kamctl/kamctl.fifo

+ 3 - 1
etc/kamailio.cfg

@@ -1,6 +1,6 @@
 #!KAMAILIO
 #!KAMAILIO
 #
 #
-# Kamailio (OpenSER) SIP Server v3.2 - default configuration script
+# Kamailio (OpenSER) SIP Server v3.3 - default configuration script
 #     - web: http://www.kamailio.org
 #     - web: http://www.kamailio.org
 #     - git: http://sip-router.org
 #     - git: http://sip-router.org
 #
 #
@@ -306,6 +306,8 @@ modparam("registrar", "method_filtering", 1)
 #modparam("registrar", "max_contacts", 10)
 #modparam("registrar", "max_contacts", 10)
 # max value for expires of registrations
 # max value for expires of registrations
 modparam("registrar", "max_expires", 3600)
 modparam("registrar", "max_expires", 3600)
+# set it to 1 to enable GRUU
+modparam("registrar", "gruu_enabled", 0)
 
 
 
 
 # ----- acc params -----
 # ----- acc params -----

+ 1 - 1
lib/srdb1/db_op.h

@@ -41,7 +41,7 @@
 /** operator greater than equal */
 /** operator greater than equal */
 #define OP_GEQ ">="
 #define OP_GEQ ">="
 /** operator negation */
 /** operator negation */
-#define OP_NEQ "!="
+#define OP_NEQ "<>"
 /** bitwise AND */
 /** bitwise AND */
 #define OP_BITWISE_AND "&"
 #define OP_BITWISE_AND "&"
 
 

+ 1 - 1
lib/srdb1/schema/silo.xml

@@ -100,7 +100,7 @@
         <name>extra_hdrs</name>
         <name>extra_hdrs</name>
         <type>text</type>
         <type>text</type>
         <default/>
         <default/>
-        <description>iExtra headers that must be restored</description>
+        <description>Extra headers that must be restored</description>
     </column>
     </column>
 
 
     <column id="callid">
     <column id="callid">

+ 0 - 1
modules/auth_identity/auth_http.c

@@ -31,7 +31,6 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
 #include <curl/curl.h>
 #include <curl/curl.h>
-#include <curl/types.h>
 #include <curl/easy.h>
 #include <curl/easy.h>
 #include <openssl/pem.h>
 #include <openssl/pem.h>
 #include <openssl/err.h>
 #include <openssl/err.h>

+ 0 - 1
modules/auth_identity/auth_identity.c

@@ -47,7 +47,6 @@
 #include <openssl/sha.h>
 #include <openssl/sha.h>
 
 
 #include <curl/curl.h>
 #include <curl/curl.h>
-#include <curl/types.h>
 #include <curl/easy.h>
 #include <curl/easy.h>
 
 
 #include "../../dprint.h"
 #include "../../dprint.h"

+ 27 - 12
modules/db_postgres/km_dbase.c

@@ -163,7 +163,7 @@ void db_postgres_close(db1_con_t* _h)
  */
  */
 static int db_postgres_submit_query(const db1_con_t* _con, const str* _s)
 static int db_postgres_submit_query(const db1_con_t* _con, const str* _s)
 {
 {
-	int i;
+	int i, retries;
 	ExecStatusType pqresult;
 	ExecStatusType pqresult;
 
 
 	if(! _con || !_s || !_s->s)
 	if(! _con || !_s || !_s->s)
@@ -194,7 +194,12 @@ static int db_postgres_submit_query(const db1_con_t* _con, const str* _s)
 			return -1;
 			return -1;
 	}
 	}
 
 
-	for(i = 0; i <= pg_retries; i++) {
+	if (CON_TRANSACTION(_con) == 1)
+		retries = 0;
+	else
+		retries = pg_retries;
+
+	for(i = 0; i <= retries; i++) {
 		/* free any previous query that is laying about */
 		/* free any previous query that is laying about */
 		db_postgres_free_query(_con);
 		db_postgres_free_query(_con);
 		/* exec the query */
 		/* exec the query */
@@ -543,15 +548,19 @@ int db_postgres_insert(const db1_con_t* _h, const db_key_t* _k, const db_val_t*
 {
 {
 	db1_res_t* _r = NULL;
 	db1_res_t* _r = NULL;
 
 
-	int tmp = db_do_insert(_h, _k, _v, _n, db_postgres_val2str, db_postgres_submit_query);
+	int ret = db_do_insert(_h, _k, _v, _n, db_postgres_val2str, db_postgres_submit_query);
 	// finish the async query, otherwise the next query will not complete
 	// finish the async query, otherwise the next query will not complete
-	if (db_postgres_store_result(_h, &_r) != 0)
+	int tmp = db_postgres_store_result(_h, &_r);
+
+	if (tmp < 0) {
 		LM_WARN("unexpected result returned");
 		LM_WARN("unexpected result returned");
+		ret = tmp;
+	}
 	
 	
 	if (_r)
 	if (_r)
 		db_free_result(_r);
 		db_free_result(_r);
 
 
-	return tmp;
+	return ret;
 }
 }
 
 
 
 
@@ -568,16 +577,19 @@ int db_postgres_delete(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _
 		const db_val_t* _v, const int _n)
 		const db_val_t* _v, const int _n)
 {
 {
 	db1_res_t* _r = NULL;
 	db1_res_t* _r = NULL;
-	int tmp = db_do_delete(_h, _k, _o, _v, _n, db_postgres_val2str,
+	int ret = db_do_delete(_h, _k, _o, _v, _n, db_postgres_val2str,
 		db_postgres_submit_query);
 		db_postgres_submit_query);
+	int tmp = db_postgres_store_result(_h, &_r);
 
 
-	if (db_postgres_store_result(_h, &_r) != 0)
+	if (tmp < 0) {
 		LM_WARN("unexpected result returned");
 		LM_WARN("unexpected result returned");
-	
+		ret = tmp;
+	}
+
 	if (_r)
 	if (_r)
 		db_free_result(_r);
 		db_free_result(_r);
 
 
-	return tmp;
+	return ret;
 }
 }
 
 
 
 
@@ -598,16 +610,19 @@ int db_postgres_update(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _
 		const int _un)
 		const int _un)
 {
 {
 	db1_res_t* _r = NULL;
 	db1_res_t* _r = NULL;
-	int tmp = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_postgres_val2str,
+	int ret = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_postgres_val2str,
 		db_postgres_submit_query);
 		db_postgres_submit_query);
+	int tmp = db_postgres_store_result(_h, &_r);
 
 
-	if (db_postgres_store_result(_h, &_r) != 0)
+	if (tmp < 0) {
 		LM_WARN("unexpected result returned");
 		LM_WARN("unexpected result returned");
+		ret = tmp;
+	}
 	
 	
 	if (_r)
 	if (_r)
 		db_free_result(_r);
 		db_free_result(_r);
 
 
-	return tmp;
+	return ret;
 }
 }
 
 
 /**
 /**

+ 1 - 1
modules_k/dialog/dlg_cb.h

@@ -76,7 +76,7 @@ typedef str* (*get_dlg_variable_f)( struct dlg_cell* dlg,
 #define DLGCB_RPC_CONTEXT     (1<<12)
 #define DLGCB_RPC_CONTEXT     (1<<12)
 #define DLGCB_DESTROY         (1<<13)
 #define DLGCB_DESTROY         (1<<13)
 #define DLGCB_SPIRALED        (1<<14)
 #define DLGCB_SPIRALED        (1<<14)
-#define DLGCB_TERMINATED_CONFIRMED (1<<14)
+#define DLGCB_TERMINATED_CONFIRMED (1<<15)
 
 
 struct dlg_callback {
 struct dlg_callback {
 	int types;
 	int types;

+ 5 - 4
modules_k/dialog/dlg_hash.c

@@ -674,11 +674,12 @@ static inline struct dlg_cell* internal_get_dlg(unsigned int h_entry,
 struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir)
 struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir)
 {
 {
 	struct dlg_cell *dlg;
 	struct dlg_cell *dlg;
+	unsigned int he;
 
 
-	if ((dlg = internal_get_dlg(core_hash(callid, 0,
-			d_table->size), callid, ftag, ttag, dir)) == 0 &&
-			(dlg = internal_get_dlg(core_hash(callid, ttag->len
-			?ttag:0, d_table->size), callid, ftag, ttag, dir)) == 0) {
+	he = core_hash(callid, 0, d_table->size);
+	dlg = internal_get_dlg(he, callid, ftag, ttag, dir);
+
+	if (dlg == 0) {
 		LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s);
 		LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s);
 		return 0;
 		return 0;
 	}
 	}

+ 14 - 9
modules_k/msilo/msilo.c

@@ -1112,17 +1112,22 @@ static int m_dump(struct sip_msg* msg, str* owner_s)
 		}
 		}
 
 
 		tmp_extra_hdrs.len = extra_hdrs_str.len+str_vals[4].len;
 		tmp_extra_hdrs.len = extra_hdrs_str.len+str_vals[4].len;
-		if ((tmp_extra_hdrs.s = pkg_malloc(tmp_extra_hdrs.len)) == NULL)
+		if(tmp_extra_hdrs.len>0)
 		{
 		{
-			LM_ERR("Out of pkg memory");
-			if (msilo_dbf.free_result(db_con, db_res) < 0)
-				LM_ERR("failed to free the query result\n");
-			msg_list_set_flag(ml, mid, MS_MSG_ERRO);
-			goto error;
+			if ((tmp_extra_hdrs.s = pkg_malloc(tmp_extra_hdrs.len)) == NULL)
+			{
+				LM_ERR("Out of pkg memory");
+				if (msilo_dbf.free_result(db_con, db_res) < 0)
+					LM_ERR("failed to free the query result\n");
+				msg_list_set_flag(ml, mid, MS_MSG_ERRO);
+				goto error;
+			}
+			memcpy(tmp_extra_hdrs.s, extra_hdrs_str.s, extra_hdrs_str.len);
+			memcpy(tmp_extra_hdrs.s+extra_hdrs_str.len, str_vals[4].s, str_vals[4].len);
+		} else {
+			tmp_extra_hdrs.len = 0;
+			tmp_extra_hdrs.s = "";
 		}
 		}
-		memcpy(tmp_extra_hdrs.s, extra_hdrs_str.s, extra_hdrs_str.len);
-		memcpy(tmp_extra_hdrs.s+extra_hdrs_str.len, str_vals[4].s, str_vals[4].len);
-		
 		hdr_str.len = 1024;
 		hdr_str.len = 1024;
 		if(m_build_headers(&hdr_str, str_vals[3] /*ctype*/,
 		if(m_build_headers(&hdr_str, str_vals[3] /*ctype*/,
 				   str_vals[0]/*from*/, rtime /*Date*/,
 				   str_vals[0]/*from*/, rtime /*Date*/,

+ 156 - 90
modules_k/presence/README

@@ -41,19 +41,22 @@ Juha Heinanen
               3.6. xcap_table(str)
               3.6. xcap_table(str)
               3.7. clean_period (int)
               3.7. clean_period (int)
               3.8. db_update_period (int)
               3.8. db_update_period (int)
-              3.9. to_tag_pref (str)
-              3.10. expires_offset (int)
-              3.11. max_expires (int)
-              3.12. server_address (str)
-              3.13. subs_db_mode (int)
-              3.14. publ_cache (int)
-              3.15. subs_htable_size (int)
-              3.16. pres_htable_size (int)
-              3.17. send_fast_notify (int)
-              3.18. enable_sphere_check (int)
-              3.19. timeout_rm_subs (int)
-              3.20. fetch_rows (integer)
-              3.21. integrated_xcap_server (integer)
+              3.9. waitn_time (int)
+              3.10. notifier_poll_rate (int)
+              3.11. notifier_processes (int)
+              3.12. to_tag_pref (str)
+              3.13. expires_offset (int)
+              3.14. max_expires (int)
+              3.15. server_address (str)
+              3.16. subs_db_mode (int)
+              3.17. publ_cache (int)
+              3.18. subs_htable_size (int)
+              3.19. pres_htable_size (int)
+              3.20. send_fast_notify (int)
+              3.21. enable_sphere_check (int)
+              3.22. timeout_rm_subs (int)
+              3.23. fetch_rows (integer)
+              3.24. integrated_xcap_server (integer)
 
 
         4. Functions
         4. Functions
 
 
@@ -101,25 +104,28 @@ Juha Heinanen
    1.6. Set xcap_table parameter
    1.6. Set xcap_table parameter
    1.7. Set clean_period parameter
    1.7. Set clean_period parameter
    1.8. Set db_update_period parameter
    1.8. Set db_update_period parameter
-   1.9. Set to_tag_pref parameter
-   1.10. Set expires_offset parameter
-   1.11. Set max_expires parameter
-   1.12. Set server_address parameter
-   1.13. Set subs_db_mode parameter
-   1.14. Set publ_cache parameter
-   1.15. Set subs_htable_size parameter
-   1.16. Set pres_htable_size parameter
-   1.17. Set send_fast_notify parameter
-   1.18. Set enable_sphere_check parameter
-   1.19. Set timeout_rm_subs parameter
-   1.20. Set fetch_rows parameter
-   1.21. Set integrated_xcap_server parameter
-   1.22. handle_publish usage
-   1.23. handle_subscribe usage
-   1.24. pres_auth_status usage
-   1.25. pres_refresh_watchers usage
-   1.26. pres_update_watchers usage
-   1.27. pres_update_presentity usage
+   1.9. Set waitn_time parameter
+   1.10. Set notifier_poll_rate parameter
+   1.11. Set notifier_processes parameter
+   1.12. Set to_tag_pref parameter
+   1.13. Set expires_offset parameter
+   1.14. Set max_expires parameter
+   1.15. Set server_address parameter
+   1.16. Set subs_db_mode parameter
+   1.17. Set publ_cache parameter
+   1.18. Set subs_htable_size parameter
+   1.19. Set pres_htable_size parameter
+   1.20. Set send_fast_notify parameter
+   1.21. Set enable_sphere_check parameter
+   1.22. Set timeout_rm_subs parameter
+   1.23. Set fetch_rows parameter
+   1.24. Set integrated_xcap_server parameter
+   1.25. handle_publish usage
+   1.26. handle_subscribe usage
+   1.27. pres_auth_status usage
+   1.28. pres_refresh_watchers usage
+   1.29. pres_update_watchers usage
+   1.30. pres_update_presentity usage
    2.1. presence_api_t structure
    2.1. presence_api_t structure
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
@@ -142,19 +148,22 @@ Chapter 1. Admin Guide
         3.6. xcap_table(str)
         3.6. xcap_table(str)
         3.7. clean_period (int)
         3.7. clean_period (int)
         3.8. db_update_period (int)
         3.8. db_update_period (int)
-        3.9. to_tag_pref (str)
-        3.10. expires_offset (int)
-        3.11. max_expires (int)
-        3.12. server_address (str)
-        3.13. subs_db_mode (int)
-        3.14. publ_cache (int)
-        3.15. subs_htable_size (int)
-        3.16. pres_htable_size (int)
-        3.17. send_fast_notify (int)
-        3.18. enable_sphere_check (int)
-        3.19. timeout_rm_subs (int)
-        3.20. fetch_rows (integer)
-        3.21. integrated_xcap_server (integer)
+        3.9. waitn_time (int)
+        3.10. notifier_poll_rate (int)
+        3.11. notifier_processes (int)
+        3.12. to_tag_pref (str)
+        3.13. expires_offset (int)
+        3.14. max_expires (int)
+        3.15. server_address (str)
+        3.16. subs_db_mode (int)
+        3.17. publ_cache (int)
+        3.18. subs_htable_size (int)
+        3.19. pres_htable_size (int)
+        3.20. send_fast_notify (int)
+        3.21. enable_sphere_check (int)
+        3.22. timeout_rm_subs (int)
+        3.23. fetch_rows (integer)
+        3.24. integrated_xcap_server (integer)
 
 
    4. Functions
    4. Functions
 
 
@@ -224,19 +233,22 @@ Chapter 1. Admin Guide
    3.6. xcap_table(str)
    3.6. xcap_table(str)
    3.7. clean_period (int)
    3.7. clean_period (int)
    3.8. db_update_period (int)
    3.8. db_update_period (int)
-   3.9. to_tag_pref (str)
-   3.10. expires_offset (int)
-   3.11. max_expires (int)
-   3.12. server_address (str)
-   3.13. subs_db_mode (int)
-   3.14. publ_cache (int)
-   3.15. subs_htable_size (int)
-   3.16. pres_htable_size (int)
-   3.17. send_fast_notify (int)
-   3.18. enable_sphere_check (int)
-   3.19. timeout_rm_subs (int)
-   3.20. fetch_rows (integer)
-   3.21. integrated_xcap_server (integer)
+   3.9. waitn_time (int)
+   3.10. notifier_poll_rate (int)
+   3.11. notifier_processes (int)
+   3.12. to_tag_pref (str)
+   3.13. expires_offset (int)
+   3.14. max_expires (int)
+   3.15. server_address (str)
+   3.16. subs_db_mode (int)
+   3.17. publ_cache (int)
+   3.18. subs_htable_size (int)
+   3.19. pres_htable_size (int)
+   3.20. send_fast_notify (int)
+   3.21. enable_sphere_check (int)
+   3.22. timeout_rm_subs (int)
+   3.23. fetch_rows (integer)
+   3.24. integrated_xcap_server (integer)
 
 
 3.1. db_url(str)
 3.1. db_url(str)
 
 
@@ -341,19 +353,73 @@ modparam("presence", "clean_period", 100)
 modparam("presence", "db_update_period", 100)
 modparam("presence", "db_update_period", 100)
 ...
 ...
 
 
-3.9. to_tag_pref (str)
+3.9. waitn_time (int)
+
+   The maximum time period that NOTIFY requests will be buffered for. The
+   server will attempt to send NOTIFY requests within many seconds of a
+   change occurring.
+
+   Note: this parameter is only used when notifier_processes is greater
+   than 0. When notifier_processes is less than or equal to 0 NOTIFY
+   requests are sent immediately.
+
+   Default value is “5”.
+
+   Example 1.9. Set waitn_time parameter
+...
+modparam("presence", "waitn_time", 10)
+...
+
+3.10. notifier_poll_rate (int)
+
+   The number of times per second that the notifier processes should check
+   for work. Approximately 1/(waitn_time * notifier_poll_rate *
+   notifier_processes) of the pending updates will be sent each time a
+   notifier process runs.
+
+   Separate notifier processes are only run when subs_db_mode is 3 (DB
+   only mode).
+
+   Default value is “10”.
+
+   Example 1.10. Set notifier_poll_rate parameter
+...
+modparam("presence", "notifier_poll_rate", 20)
+...
+
+3.11. notifier_processes (int)
+
+   The number of notifier processes that should be started.
+
+   Separate notifier processes are only run when subs_db_mode is 3 (DB
+   only mode).
+
+   Note: setting this parameter to 0 when subs_db_mode is 3 keeps the old
+   behaviour (sending NOTIFY requests immediately). This (old) behaviour
+   is disabled by default in DB only mode because under load, when lots of
+   NOTIFY requests can be sent on a dialog at the same time, there are
+   race conditions which result in CSeq re-use.
+
+   Default value is “1”.
+
+   Example 1.11. Set notifier_processes parameter
+...
+modparam("presence", "notifier_processes", 2)
+...
+
+3.12. to_tag_pref (str)
 
 
    The prefix used when generating to_tag when sending replies for
    The prefix used when generating to_tag when sending replies for
    SUBSCRIBE requests.
    SUBSCRIBE requests.
 
 
    Default value is “10”.
    Default value is “10”.
 
 
-   Example 1.9. Set to_tag_pref parameter
+   Example 1.12. Set to_tag_pref parameter
 ...
 ...
 modparam("presence", "to_tag_pref", 'pres')
 modparam("presence", "to_tag_pref", 'pres')
 ...
 ...
 
 
-3.10. expires_offset (int)
+3.13. expires_offset (int)
 
 
    The value in seconds that should be subtracted from the expires value
    The value in seconds that should be subtracted from the expires value
    when sending a 200OK for a publish. It is used for forcing the client
    when sending a 200OK for a publish. It is used for forcing the client
@@ -361,35 +427,35 @@ modparam("presence", "to_tag_pref", 'pres')
 
 
    Default value is “0”.
    Default value is “0”.
 
 
-   Example 1.10. Set expires_offset parameter
+   Example 1.13. Set expires_offset parameter
 ...
 ...
 modparam("presence", "expires_offset", 10)
 modparam("presence", "expires_offset", 10)
 ...
 ...
 
 
-3.11. max_expires (int)
+3.14. max_expires (int)
 
 
    The the maximum admissible expires value for PUBLISH/SUBSCRIBE message
    The the maximum admissible expires value for PUBLISH/SUBSCRIBE message
    (in seconds).
    (in seconds).
 
 
    Default value is “3600”.
    Default value is “3600”.
 
 
-   Example 1.11. Set max_expires parameter
+   Example 1.14. Set max_expires parameter
 ...
 ...
 modparam("presence", "max_expires", 3600)
 modparam("presence", "max_expires", 3600)
 ...
 ...
 
 
-3.12. server_address (str)
+3.15. server_address (str)
 
 
    The presence server address which will become the value of Contact
    The presence server address which will become the value of Contact
    header filed for 200 OK replies to SUBSCRIBE and PUBLISH and in NOTIFY
    header filed for 200 OK replies to SUBSCRIBE and PUBLISH and in NOTIFY
    messages.
    messages.
 
 
-   Example 1.12. Set server_address parameter
+   Example 1.15. Set server_address parameter
 ...
 ...
 modparam("presence", "server_address", "sip:10.10.10.10:5060")
 modparam("presence", "server_address", "sip:10.10.10.10:5060")
 ...
 ...
 
 
-3.13. subs_db_mode (int)
+3.16. subs_db_mode (int)
 
 
    The presence module can utilize database for persistent subscription
    The presence module can utilize database for persistent subscription
    storage. If you use database, your subscriptions will survive machine
    storage. If you use database, your subscriptions will survive machine
@@ -423,12 +489,12 @@ modparam("presence", "server_address", "sip:10.10.10.10:5060")
 
 
    Default value is 2 (Write-Back scheme).
    Default value is 2 (Write-Back scheme).
 
 
-   Example 1.13. Set subs_db_mode parameter
+   Example 1.16. Set subs_db_mode parameter
 ...
 ...
 modparam("presence", "subs_db_mode", 1)
 modparam("presence", "subs_db_mode", 1)
 ...
 ...
 
 
-3.14. publ_cache (int)
+3.17. publ_cache (int)
 
 
    To improve performance, the presence module holds by default a publish
    To improve performance, the presence module holds by default a publish
    cache that says if a certain publication exists in database. This is
    cache that says if a certain publication exists in database. This is
@@ -444,12 +510,12 @@ modparam("presence", "subs_db_mode", 1)
 
 
    Default value is “1”.
    Default value is “1”.
 
 
-   Example 1.14. Set publ_cache parameter
+   Example 1.17. Set publ_cache parameter
 ...
 ...
 modparam("presence", "publ_cache", 0)
 modparam("presence", "publ_cache", 0)
 ...
 ...
 
 
-3.15. subs_htable_size (int)
+3.18. subs_htable_size (int)
 
 
    The size of the in-memory hash table to store subscription dialogs.
    The size of the in-memory hash table to store subscription dialogs.
    This parameter will be used as the power of 2 when computing table
    This parameter will be used as the power of 2 when computing table
@@ -457,24 +523,24 @@ modparam("presence", "publ_cache", 0)
 
 
    Default value is “9 (512)”.
    Default value is “9 (512)”.
 
 
-   Example 1.15. Set subs_htable_size parameter
+   Example 1.18. Set subs_htable_size parameter
 ...
 ...
 modparam("presence", "subs_htable_size", 11)
 modparam("presence", "subs_htable_size", 11)
 ...
 ...
 
 
-3.16. pres_htable_size (int)
+3.19. pres_htable_size (int)
 
 
    The size of the in-memory hash table to store publish records. This
    The size of the in-memory hash table to store publish records. This
    parameter will be used as the power of 2 when computing table size.
    parameter will be used as the power of 2 when computing table size.
 
 
    Default value is “9 (512)”.
    Default value is “9 (512)”.
 
 
-   Example 1.16. Set pres_htable_size parameter
+   Example 1.19. Set pres_htable_size parameter
 ...
 ...
 modparam("presence", "pres_htable_size", 11)
 modparam("presence", "pres_htable_size", 11)
 ...
 ...
 
 
-3.17. send_fast_notify (int)
+3.20. send_fast_notify (int)
 
 
    This parameter enables or disables the sending of an initial empty
    This parameter enables or disables the sending of an initial empty
    NOTIFY after a SUBSCRIBE/reSUBSCRIBE. This caused problems for MWI
    NOTIFY after a SUBSCRIBE/reSUBSCRIBE. This caused problems for MWI
@@ -484,12 +550,12 @@ modparam("presence", "pres_htable_size", 11)
 
 
    Default value is “1 ”.
    Default value is “1 ”.
 
 
-   Example 1.17. Set send_fast_notify parameter
+   Example 1.20. Set send_fast_notify parameter
 ...
 ...
 modparam("presence", "send_fast_notify", 0)
 modparam("presence", "send_fast_notify", 0)
 ...
 ...
 
 
-3.18. enable_sphere_check (int)
+3.21. enable_sphere_check (int)
 
 
    This parameter is a flag that should be set if permission rules include
    This parameter is a flag that should be set if permission rules include
    sphere checking. The sphere information is expected to be present in
    sphere checking. The sphere information is expected to be present in
@@ -499,12 +565,12 @@ modparam("presence", "send_fast_notify", 0)
 
 
    Default value is “0 ”.
    Default value is “0 ”.
 
 
-   Example 1.18. Set enable_sphere_check parameter
+   Example 1.21. Set enable_sphere_check parameter
 ...
 ...
 modparam("presence", "enable_sphere_check", 1)
 modparam("presence", "enable_sphere_check", 1)
 ...
 ...
 
 
-3.19. timeout_rm_subs (int)
+3.22. timeout_rm_subs (int)
 
 
    This parameter is a flag that should be set if subscriptions should be
    This parameter is a flag that should be set if subscriptions should be
    removed from the active_watchers when a NOTIFY times out. RFC3265
    removed from the active_watchers when a NOTIFY times out. RFC3265
@@ -514,23 +580,23 @@ modparam("presence", "enable_sphere_check", 1)
 
 
    Default value is “1”.
    Default value is “1”.
 
 
-   Example 1.19. Set timeout_rm_subs parameter
+   Example 1.22. Set timeout_rm_subs parameter
 ...
 ...
 modparam("presence", "timeout_rm_subs", 0)
 modparam("presence", "timeout_rm_subs", 0)
 ...
 ...
 
 
-3.20. fetch_rows (integer)
+3.23. fetch_rows (integer)
 
 
    Number of rows to be loaded in one step from database.
    Number of rows to be loaded in one step from database.
 
 
    Default value is 500.
    Default value is 500.
 
 
-   Example 1.20. Set fetch_rows parameter
+   Example 1.23. Set fetch_rows parameter
 ...
 ...
 modparam("presence", "fetch_rows", 1000)
 modparam("presence", "fetch_rows", 1000)
 ...
 ...
 
 
-3.21. integrated_xcap_server (integer)
+3.24. integrated_xcap_server (integer)
 
 
    Enable the integrated XCAP server connection. This is only required if
    Enable the integrated XCAP server connection. This is only required if
    you want to use the pres_update_presentity() function. Setting to 0
    you want to use the pres_update_presentity() function. Setting to 0
@@ -539,7 +605,7 @@ modparam("presence", "fetch_rows", 1000)
 
 
    Default value is 0.
    Default value is 0.
 
 
-   Example 1.21. Set integrated_xcap_server parameter
+   Example 1.24. Set integrated_xcap_server parameter
 ...
 ...
 modparam("presence", "integrated_xcap_server", 1)
 modparam("presence", "integrated_xcap_server", 1)
 ...
 ...
@@ -573,7 +639,7 @@ modparam("presence", "integrated_xcap_server", 1)
 
 
    The module sends an appropriate stateless reply in all cases.
    The module sends an appropriate stateless reply in all cases.
 
 
-   Example 1.22. handle_publish usage
+   Example 1.25. handle_publish usage
 ...
 ...
         if(is_method("PUBLISH"))
         if(is_method("PUBLISH"))
         {
         {
@@ -604,7 +670,7 @@ modparam("presence", "integrated_xcap_server", 1)
 
 
    The module sends an appropriate stateless reply in all cases.
    The module sends an appropriate stateless reply in all cases.
 
 
-   Example 1.23. handle_subscribe usage
+   Example 1.26. handle_subscribe usage
 ...
 ...
 if(method=="SUBSCRIBE")
 if(method=="SUBSCRIBE")
     handle_subscribe();
     handle_subscribe();
@@ -621,7 +687,7 @@ if(method=="SUBSCRIBE")
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 
-   Example 1.24. pres_auth_status usage
+   Example 1.27. pres_auth_status usage
 ...
 ...
 if (method=="MESSAGE") {
 if (method=="MESSAGE") {
     pres_auth_status("$fu", $ru");
     pres_auth_status("$fu", $ru");
@@ -652,7 +718,7 @@ if (method=="MESSAGE") {
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.25. pres_refresh_watchers usage
+   Example 1.28. pres_refresh_watchers usage
 ...
 ...
 pres_refresh_watchers("sip:[email protected]", "presence", 1);
 pres_refresh_watchers("sip:[email protected]", "presence", 1);
 ...
 ...
@@ -670,7 +736,7 @@ pres_refresh_watchers("sip:[email protected]", "presence", 1);
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.26. pres_update_watchers usage
+   Example 1.29. pres_update_watchers usage
 ...
 ...
 pres_update_watchers("sip:[email protected]", "presence");
 pres_update_watchers("sip:[email protected]", "presence");
 ...
 ...
@@ -689,7 +755,7 @@ pres_update_watchers("sip:[email protected]", "presence");
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.27. pres_update_presentity usage
+   Example 1.30. pres_update_presentity usage
 ...
 ...
 pres_update_presentity("$xcapuri(u=>xuid)", "$xcapuri(u=>uri_adoc)", "$xcapuri(u
 pres_update_presentity("$xcapuri(u=>xuid)", "$xcapuri(u=>uri_adoc)", "$xcapuri(u
 =>file)");
 =>file)");

+ 84 - 1
modules_k/presence/doc/presence_admin.xml

@@ -239,7 +239,90 @@ modparam("presence", "db_update_period", 100)
 		</example>
 		</example>
 	</section>
 	</section>
 
 
-<section>
+	<section>
+		<title><varname>waitn_time</varname> (int)</title>
+		<para>
+		The maximum time period that NOTIFY requests will
+		be buffered for.  The server will attempt to send
+		NOTIFY requests within many seconds of a change occurring.
+		</para>
+		<para>
+		Note: this parameter is only used when notifier_processes is
+		greater than 0. When notifier_processes is less than or equal
+		to 0 NOTIFY requests are sent immediately.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>5</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>waitn_time</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("presence", "waitn_time", 10)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>notifier_poll_rate</varname> (int)</title>
+		<para>
+		The number of times per second that the notifier processes
+		should check for work.  Approximately
+		1/(waitn_time * notifier_poll_rate * notifier_processes) of the
+		pending updates will be sent each time a notifier process runs.
+		</para>
+		<para>
+		Separate notifier processes are only run when subs_db_mode is 3
+		(DB only mode).
+		</para>
+		<para>
+		<emphasis>Default value is <quote>10</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>notifier_poll_rate</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("presence", "notifier_poll_rate", 20)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>notifier_processes</varname> (int)</title>
+		<para>
+		The number of notifier processes that should be started.
+		</para>
+		<para>
+		Separate notifier processes are only run when subs_db_mode is
+		3 (DB only mode).
+		</para>
+		<para>
+		Note: setting this parameter to 0 when subs_db_mode is 3
+		keeps the old behaviour (sending NOTIFY requests immediately).
+		This (old) behaviour is disabled by default in DB only mode
+		because under load, when lots of NOTIFY requests can be sent
+		on a dialog at the same time, there are race conditions which
+		result in CSeq re-use.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>1</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>notifier_processes</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("presence", "notifier_processes", 2)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
 		<title><varname>to_tag_pref</varname> (str)</title>
 		<title><varname>to_tag_pref</varname> (str)</title>
 		<para>
 		<para>
 		The prefix used when generating to_tag when sending replies for
 		The prefix used when generating to_tag when sending replies for

+ 1029 - 25
modules_k/presence/notify.c

@@ -98,6 +98,8 @@ str str_sender_col = str_init("sender");
 str str_updated_col = str_init("updated");
 str str_updated_col = str_init("updated");
 str str_updated_winfo_col = str_init("updated_winfo");
 str str_updated_winfo_col = str_init("updated_winfo");
 
 
+int subset=0;
+
 char* get_status_str(int status_flag)
 char* get_status_str(int status_flag)
 {
 {
 	switch(status_flag)
 	switch(status_flag)
@@ -111,22 +113,24 @@ char* get_status_str(int status_flag)
 }
 }
 
 
 void printf_subs(subs_t* subs)
 void printf_subs(subs_t* subs)
-{	
-	LM_DBG("\n\t[pres_uri]= %.*s\n\t[to_user]= %.*s\t[to_domain]= %.*s"
-		"\n\t[from_user]= %.*s\t[from_domain]= %.*s\n\t[w_user]= %.*s"
-		"\n\t[w_domain]=%.*s\t[event]=%.*s\t[status]= %s"
-		"\n\t[expires]= %u\n\t[callid]= %.*s\t[local_cseq]=%d"
-		"\n\t[to_tag]= %.*s\t[from_tag]= %.*s""\n\t[contact]= %.*s"
-		"\t[record_route]= %.*s\n",subs->pres_uri.len,subs->pres_uri.s,
-		subs->to_user.len,subs->to_user.s,subs->to_domain.len,
-		subs->to_domain.s,subs->from_user.len,subs->from_user.s,
-		subs->from_domain.len,subs->from_domain.s,subs->watcher_user.len,
-		subs->watcher_user.s,subs->watcher_domain.len,subs->watcher_domain.s,
-		subs->event->name.len,subs->event->name.s,get_status_str(subs->status),
-		subs->expires,subs->callid.len,subs->callid.s,subs->local_cseq,
-		subs->to_tag.len,subs->to_tag.s,subs->from_tag.len, subs->from_tag.s,
-		subs->contact.len,subs->contact.s,subs->record_route.len,
-		subs->record_route.s);
+{
+	LM_DBG("pres_uri: %.*s\n", subs->pres_uri.len, subs->pres_uri.s);
+	LM_DBG("watcher_user@watcher_domain: %.*s@%.*s\n", subs->watcher_user.len, subs->watcher_user.s, subs->watcher_domain.len, subs->watcher_domain.s);
+	LM_DBG("to_user@to_domain: %.*s@%.*s\n", subs->to_user.len, subs->to_user.s, subs->to_domain.len, subs->to_domain.s);
+	LM_DBG("from_user@from_domain: %.*s@%.*s\n", subs->from_user.len, subs->from_user.s, subs->from_domain.len, subs->from_domain.s);
+	LM_DBG("callid/from_tag/to_tag: %.*s/%.*s/%.*s\n", subs->callid.len, subs->callid.s, subs->from_tag.len, subs->from_tag.s, subs->to_tag.len, subs->to_tag.s);
+	LM_DBG("local_cseq/remote_cseq: %u/%u\n", subs->local_cseq, subs->remote_cseq);
+	LM_DBG("local_contact/contact: %.*s/%.*s\n", subs->local_contact.len, subs->local_contact.s, subs->contact.len, subs->contact.s);
+	LM_DBG("record_route: %.*s\n", subs->record_route.len, subs->record_route.s);
+	LM_DBG("sockinfo_str: %.*s\n", subs->sockinfo_str.len, subs->sockinfo_str.s);
+	
+	LM_DBG("event: %.*s\n", subs->event->name.len, subs->event->name.s);
+	LM_DBG("status: %s\n", get_status_str(subs->status));
+	LM_DBG("reason: %.*s\n", subs->reason.len, subs->reason.s);
+	LM_DBG("version: %u\n", subs->version);
+	LM_DBG("expires: %u\n", subs->expires);
+
+	LM_DBG("updated/updated_winfo: %d/%d\n", subs->updated, subs->updated_winfo);
 }
 }
 
 
 int build_str_hdr(subs_t* subs, int is_body, str* hdr)
 int build_str_hdr(subs_t* subs, int is_body, str* hdr)
@@ -232,10 +236,10 @@ int build_str_hdr(subs_t* subs, int is_body, str* hdr)
 int get_wi_subs_db(subs_t* subs, watcher_t* watchers)
 int get_wi_subs_db(subs_t* subs, watcher_t* watchers)
 {	
 {	
 	subs_t sb;
 	subs_t sb;
-	db_key_t query_cols[6];
-	db_op_t  query_ops[6];
-	db_val_t query_vals[6];
-	db_key_t result_cols[6];
+	db_key_t query_cols[3];
+	db_op_t  query_ops[3];
+	db_val_t query_vals[3];
+	db_key_t result_cols[5];
 	db1_res_t *result = NULL;
 	db1_res_t *result = NULL;
 	db_row_t *row = NULL ;	
 	db_row_t *row = NULL ;	
 	db_val_t *row_vals = NULL;
 	db_val_t *row_vals = NULL;
@@ -258,6 +262,13 @@ int get_wi_subs_db(subs_t* subs, watcher_t* watchers)
 	query_vals[n_query_cols].val.str_val = subs->event->wipeer->name;
 	query_vals[n_query_cols].val.str_val = subs->event->wipeer->name;
 	n_query_cols++;
 	n_query_cols++;
 
 
+	query_cols[n_query_cols] = &str_expires_col;
+	query_ops[n_query_cols] = OP_GT;
+	query_vals[n_query_cols].type = DB1_INT;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.int_val = (int)time(NULL) - expires_offset;
+	n_query_cols++;
+
 	result_cols[status_col=n_result_cols++] = &str_status_col;
 	result_cols[status_col=n_result_cols++] = &str_status_col;
 	result_cols[expires_col=n_result_cols++] = &str_expires_col;
 	result_cols[expires_col=n_result_cols++] = &str_expires_col;
 	result_cols[watcher_user_col=n_result_cols++] = &str_watcher_username_col;
 	result_cols[watcher_user_col=n_result_cols++] = &str_watcher_username_col;
@@ -427,6 +438,8 @@ void free_watcher_list(watcher_t* watchers)
 		watchers= watchers->next;
 		watchers= watchers->next;
 		pkg_free(w);
 		pkg_free(w);
 	}
 	}
+
+	watchers = NULL;
 }
 }
 
 
 int add_watcher_list(subs_t *s, watcher_t *watchers)
 int add_watcher_list(subs_t *s, watcher_t *watchers)
@@ -1283,7 +1296,78 @@ done:
 	free_subs_list(subs_array, PKG_MEM_TYPE, 0);
 	free_subs_list(subs_array, PKG_MEM_TYPE, 0);
 	free_notify_body(notify_body, p->event);	
 	free_notify_body(notify_body, p->event);	
 	return ret_code;
 	return ret_code;
-}	
+}
+
+int publ_notify_notifier(str pres_uri, pres_ev_t *event)
+{
+	db_key_t query_cols[2], result_cols[3];
+	db_val_t query_vals[2], *values;
+	db_row_t *rows;
+	db1_res_t *result = NULL;
+	int n_query_cols = 0, n_result_cols = 0;
+	int r_callid_col = 0, r_to_tag_col = 0, r_from_tag_col = 0;
+	int i;
+	int ret = -1;
+	subs_t subs;
+
+	if(pa_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		goto error;
+	}
+
+	if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		goto error;
+	}
+
+	query_cols[n_query_cols]= &str_presentity_uri_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = pres_uri;
+	n_query_cols++;
+
+	query_cols[n_query_cols]= &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = event->name;
+	n_query_cols++;
+
+	result_cols[r_callid_col=n_result_cols++] = &str_callid_col;
+	result_cols[r_to_tag_col=n_result_cols++] = &str_to_tag_col;
+	result_cols[r_from_tag_col=n_result_cols++] = &str_from_tag_col;
+
+	if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols, 
+				n_query_cols, n_result_cols, 0, &result )< 0)
+	{
+		LM_ERR("Can't query db\n");
+		goto error;
+	}
+
+	if(result == NULL) goto error;
+
+	for (i = 0; i <RES_ROW_N(result); i++)
+	{
+		rows = RES_ROWS(result);
+		values = ROW_VALUES(rows);
+
+		subs.callid.s = (char *) VAL_STRING(&values[r_callid_col]);
+		subs.callid.len = strlen(subs.callid.s);
+		subs.to_tag.s = (char *) VAL_STRING(&values[r_to_tag_col]);
+		subs.to_tag.len = strlen(subs.to_tag.s);
+		subs.from_tag.s = (char *) VAL_STRING(&values[r_from_tag_col]);
+		subs.from_tag.len = strlen(subs.from_tag.s);
+
+		set_updated(&subs);
+	}
+
+	ret = RES_ROW_N(result);
+
+error:
+	if (result) pa_dbf.free_result(pa_db, result);
+	return ret;
+}
 
 
 int query_db_notify(str* pres_uri, pres_ev_t* event, subs_t* watcher_subs )
 int query_db_notify(str* pres_uri, pres_ev_t* event, subs_t* watcher_subs )
 {
 {
@@ -1351,14 +1435,14 @@ int send_notify_request(subs_t* subs, subs_t * watcher_subs,
 	str str_hdr = {0, 0};
 	str str_hdr = {0, 0};
 	str* notify_body = NULL;
 	str* notify_body = NULL;
 	int result= 0;
 	int result= 0;
-    c_back_param *cb_param= NULL;
+	c_back_param *cb_param= NULL;
 	str* final_body= NULL;
 	str* final_body= NULL;
 	uac_req_t uac_r;
 	uac_req_t uac_r;
 	
 	
 	LM_DBG("dialog info:\n");
 	LM_DBG("dialog info:\n");
 	printf_subs(subs);
 	printf_subs(subs);
 
 
-    /* getting the status of the subscription */
+	/* getting the status of the subscription */
 
 
 	if(force_null_body)
 	if(force_null_body)
 	{
 	{
@@ -1541,8 +1625,9 @@ int notify(subs_t* subs, subs_t * watcher_subs,str* n_body,int force_null_body)
 			}
 			}
 		}
 		}
 		/* if DB_ONLY mode or WRITE_THROUGH update in database */
 		/* if DB_ONLY mode or WRITE_THROUGH update in database */
-		if(subs->recv_event!=PRES_SUBSCRIBE_RECV &&
-				(subs_dbmode == DB_ONLY || subs_dbmode == WRITE_THROUGH))
+		if(subs->recv_event!=PRES_SUBSCRIBE_RECV
+				&& ((subs_dbmode == DB_ONLY && pres_notifier_processes == 0)
+				    || subs_dbmode == WRITE_THROUGH))
 		{
 		{
 			LM_DBG("updating subscription to database\n");
 			LM_DBG("updating subscription to database\n");
 			if(update_subs_db(subs, LOCAL_TYPE)< 0)
 			if(update_subs_db(subs, LOCAL_TYPE)< 0)
@@ -1898,3 +1983,922 @@ error:
 
 
 }
 }
 
 
+#define EXTRACT_STRING(strng, chars)\
+			do {\
+			strng.s = (char *) chars;\
+			strng.len = strlen(strng.s);\
+			} while(0);
+
+static int unset_watchers_updated_winfo(str *pres_uri)
+{
+	db_key_t query_cols[3], result_cols[1], update_cols[1];
+	db_val_t query_vals[3], update_vals[1];
+	db_op_t query_ops[2];
+	db1_res_t *result = NULL;
+	int n_query_cols = 0;
+	int ncols, ret = -1;
+	str winfo = str_init("presence.winfo");
+
+	/* If this is the only presence.winfo dialog awaiting
+	   update for this presentity reset all of the watchers
+	   updated_winfo fields. */
+
+	query_cols[n_query_cols] = &str_presentity_uri_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val.s = pres_uri->s;
+	query_vals[n_query_cols].val.str_val.len = pres_uri->len;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = winfo;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_updated_col;
+	query_vals[n_query_cols].type = DB1_INT;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.int_val = UPDATED_TYPE;
+	n_query_cols++;
+
+	result_cols[0] = &str_id_col;
+
+	update_cols[0] = &str_updated_winfo_col;
+	update_vals[0].type = DB1_INT;
+	update_vals[0].nul = 0;
+	update_vals[0].val.int_val = NO_UPDATE_TYPE;
+
+	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+	{
+		LM_ERR("use table failed\n");
+		goto error;
+	}
+
+	if (pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols,
+					n_query_cols, 1, 0, &result) < 0)
+	{
+		LM_ERR("in sql query\n");
+		goto error;
+	}
+
+	if (result == NULL)
+		ncols = 0;
+	else
+		ncols = result->n;
+
+	if (ncols <= 1)
+	{
+		query_ops[0] = OP_EQ;
+		query_ops[1] = OP_NEQ;
+
+		if (pa_dbf.update(pa_db, query_cols, query_ops,
+				  query_vals, update_cols,
+				  update_vals, 2, 1) < 0)
+		{
+			LM_ERR("in sql query\n");
+			goto error;
+		}
+
+		ret = 1;
+	}
+	else
+		ret = 0;
+
+error:
+	if (result) pa_dbf.free_result(pa_db, result);
+	return ret;
+}
+
+static int winfo_dialog_pending(str *pres_uri)
+{
+	db_key_t query_cols[3], result_cols[1];
+	db_val_t query_vals[3];
+	db1_res_t *result = NULL;
+	int n_query_cols = 0, ret = -1;
+	str winfo = str_init("presence.winfo");
+
+	query_cols[n_query_cols] = &str_presentity_uri_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val.s = pres_uri->s;
+	query_vals[n_query_cols].val.str_val.len = pres_uri->len;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = winfo;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_updated_col;
+	query_vals[n_query_cols].type = DB1_INT;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.int_val = UPDATED_TYPE;
+	n_query_cols++;
+
+	result_cols[0] = &str_id_col;
+
+	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+	{
+		LM_ERR("use table failed\n");
+		goto error;
+	}
+
+	if (pa_dbf.query(pa_db, query_cols, 0, query_vals,
+			 result_cols, n_query_cols, 1, 0, &result) < 0)
+	{
+		LM_ERR("in sql query\n");
+		goto error;
+	}
+
+	if (result == NULL)
+		ret = 0;
+	else
+		ret = result->n;
+
+error:
+	if (result) pa_dbf.free_result(pa_db, result);
+	return ret;
+}
+
+static int watchers_awaiting_update(str *pres_uri, pres_ev_t *event)
+{
+	db_key_t query_cols[3], result_cols[1];
+	db_val_t query_vals[3];
+	db_op_t query_ops[3];
+	db1_res_t *result = NULL;
+	int n_query_cols = 0;
+	int ret = -1;
+
+	query_cols[n_query_cols] = &str_presentity_uri_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val.s = pres_uri->s;
+	query_vals[n_query_cols].val.str_val.len = pres_uri->len;
+	query_ops[n_query_cols] = OP_EQ;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = event->name;
+	query_ops[n_query_cols] = OP_EQ;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_updated_col;
+	query_vals[n_query_cols].type = DB1_INT;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.int_val = NO_UPDATE_TYPE;
+	query_ops[n_query_cols] = OP_NEQ;
+	n_query_cols++;
+
+	result_cols[0] = &str_id_col;
+
+	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+	{
+		LM_ERR("use table failed\n");
+		goto error;
+	}
+
+	if (pa_dbf.query(pa_db, query_cols, query_ops, query_vals,
+				result_cols, n_query_cols, 1, 0, &result) < 0)
+	{
+		LM_ERR("in sql query\n");
+		goto error;
+	}
+
+	if (result == NULL)
+		ret = 0;
+	else
+		ret = result->n;
+
+error:
+	if (result) pa_dbf.free_result(pa_db, result);
+	return ret;
+}
+
+int set_wipeer_subs_updated(str *pres_uri, pres_ev_t *event, int full)
+{
+	db_key_t query_cols[3], result_cols[3], update_cols[2];
+	db_val_t query_vals[3], update_vals[2], *values;
+	db_row_t *rows;
+	db1_res_t *result = NULL;
+	int n_query_cols = 0, n_result_cols = 0, n_update_cols = 0;
+	int callid_col, from_tag_col, to_tag_col;
+	int i, ret = -1;
+	str callid, from_tag, to_tag;
+
+	query_cols[n_query_cols] = &str_presentity_uri_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val.s = pres_uri->s;
+	query_vals[n_query_cols].val.str_val.len = pres_uri->len;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = event->name;
+	n_query_cols++;
+
+	result_cols[callid_col = n_result_cols++] = &str_callid_col;
+	result_cols[from_tag_col = n_result_cols++] = &str_from_tag_col;
+	result_cols[to_tag_col = n_result_cols++] = &str_to_tag_col;
+
+	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+	{
+		LM_ERR("use table failed\n");
+		goto error;
+	}
+
+	if (pa_dbf.query (pa_db, query_cols, 0, query_vals, result_cols,
+				n_query_cols, n_result_cols, 0,  &result) < 0) 
+	{
+		LM_ERR("in sql query\n");
+		goto error;
+	}
+
+	if (result == NULL )
+		goto error;
+
+	if (result->n <= 0)
+	{
+		ret = 0;
+		goto done;
+	}
+
+	rows = RES_ROWS(result);
+	for (i = 0; i < RES_ROW_N(result); i++)
+	{
+		values = ROW_VALUES(&rows[i]);
+
+		EXTRACT_STRING(callid, VAL_STRING(&values[callid_col]));
+		EXTRACT_STRING(from_tag, VAL_STRING(&values[from_tag_col]));
+		EXTRACT_STRING(to_tag, VAL_STRING(&values[to_tag_col]));
+
+		n_query_cols = 0;
+		n_update_cols = 0;
+
+		query_cols[n_query_cols] = &str_callid_col;
+		query_vals[n_query_cols].type = DB1_STR;
+		query_vals[n_query_cols].nul = 0;
+		query_vals[n_query_cols].val.str_val = callid;
+		n_query_cols++;
+	
+		query_cols[n_query_cols] = &str_to_tag_col;
+		query_vals[n_query_cols].type = DB1_STR;
+		query_vals[n_query_cols].nul = 0;
+		query_vals[n_query_cols].val.str_val = to_tag;
+		n_query_cols++;
+	
+		query_cols[n_query_cols] = &str_from_tag_col;
+		query_vals[n_query_cols].type = DB1_STR;
+		query_vals[n_query_cols].nul = 0;
+		query_vals[n_query_cols].val.str_val = from_tag;
+		n_query_cols++;
+	
+		update_cols[n_update_cols] = &str_updated_col;
+		update_vals[n_update_cols].type = DB1_INT;
+		update_vals[n_update_cols].nul = 0;
+		update_vals[n_update_cols].val.int_val = core_hash(&callid,
+			&from_tag, (pres_waitn_time * pres_notifier_poll_rate
+					* pres_notifier_processes) - 1);
+		n_update_cols++;
+
+		if (full)
+		{
+			update_cols[n_update_cols] = &str_updated_winfo_col;
+			update_vals[n_update_cols].type = DB1_INT;
+			update_vals[n_update_cols].nul = 0;
+			update_vals[n_update_cols].val.int_val = UPDATED_TYPE;
+			n_update_cols++;
+		}
+
+		if (pa_dbf.update(pa_db, query_cols, 0, query_vals, update_cols,
+				  update_vals, n_query_cols, n_update_cols) < 0)
+		{
+			LM_ERR("in sql query\n");
+			goto error;
+		}
+	}
+
+	ret = 1;
+
+done:
+error:
+	if (result) pa_dbf.free_result(pa_db, result);
+	return ret;
+}
+
+int set_updated(subs_t *sub)
+{
+	db_key_t query_cols[3], update_cols[1];
+	db_val_t query_vals[3], update_vals[1];
+	int n_query_cols = 0;
+
+	query_cols[n_query_cols] = &str_callid_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = sub->callid;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_to_tag_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = sub->to_tag;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_from_tag_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = sub->from_tag;
+	n_query_cols++;
+
+	update_cols[0] = &str_updated_col;
+	update_vals[0].type = DB1_INT;
+	update_vals[0].nul = 0;
+	update_vals[0].val.int_val = core_hash(&sub->callid, &sub->from_tag,
+				(pres_waitn_time * pres_notifier_poll_rate
+						* pres_notifier_processes) - 1);
+
+	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+	{
+		LM_ERR("use table failed\n");
+		return -1;
+	}
+
+	if (pa_dbf.update(pa_db, query_cols, 0, query_vals, update_cols,
+					update_vals, n_query_cols, 1) < 0)
+	{
+		LM_ERR("in sql query\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static watcher_t *build_watchers_list(subs_t *sub)
+{
+	db_key_t query_cols[3], result_cols[4];
+	db_val_t query_vals[3], *values;
+	db_row_t *rows;
+	db1_res_t *result;
+	int n_query_cols = 0, n_result_cols = 0;
+	int wuser_col, wdomain_col, callid_col, status_col;
+	int i;
+	subs_t sb;
+	watcher_t *watchers = NULL;
+
+	watchers = (watcher_t*) pkg_malloc(sizeof(watcher_t));
+	if (watchers == NULL)
+	{
+		ERR_MEM(PKG_MEM_STR);
+	}
+	memset(watchers, 0, sizeof(watcher_t));
+
+	query_cols[n_query_cols] = &str_presentity_uri_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val= sub->pres_uri;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = sub->event->wipeer->name;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_updated_winfo_col;
+	query_vals[n_query_cols].type = DB1_INT;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.int_val = UPDATED_TYPE;
+	n_query_cols++;
+
+	result_cols[wuser_col = n_result_cols++] = &str_watcher_username_col;
+	result_cols[wdomain_col = n_result_cols++] = &str_watcher_domain_col;
+	result_cols[callid_col = n_result_cols++] = &str_callid_col;
+	result_cols[status_col = n_result_cols++] = &str_status_col;
+
+	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+	{
+		LM_ERR("use table failed\n");
+		goto error;
+	}
+
+	if (pa_dbf.query (pa_db, query_cols, 0, query_vals, result_cols,
+				n_query_cols, n_result_cols, 0,  &result) < 0) 
+	{
+		LM_ERR("in sql query\n");
+		goto error;
+	}
+
+	if (result == NULL )
+		goto error;
+
+	if (result->n <= 0)
+		goto done;
+
+	rows = RES_ROWS(result);
+	for (i = 0; i < RES_ROW_N(result); i++)
+	{
+		memset(&sb, 0, sizeof(subs_t));
+		values = ROW_VALUES(&rows[i]);
+
+		EXTRACT_STRING(sb.watcher_user, VAL_STRING(&values[wuser_col]));
+		EXTRACT_STRING(sb.watcher_domain, VAL_STRING(&values[wdomain_col]));
+		EXTRACT_STRING(sb.callid, VAL_STRING(&values[callid_col]));
+		sb.status = VAL_INT(&values[status_col]);
+
+		sb.event = sub->event->wipeer;
+		
+		if (add_watcher_list(&sb, watchers) < 0)
+			goto error;
+	}
+	
+done:
+	pa_dbf.free_result(pa_db, result);
+	return watchers;
+
+error:
+	if (result) pa_dbf.free_result(pa_db, result);
+	free_watcher_list(watchers);
+	return NULL;
+}
+
+static int notifier_notify(subs_t *sub, int *updated)
+{
+	str *nbody = NULL;
+	watcher_t *watchers = NULL;
+	int ret = -1, attempt_delete_presentities = 0;
+
+	*updated = 0;
+
+	if (sub->expires == 0) /* Terminating dialog NOTIFY */
+	{
+		sub->status = TERMINATED_STATUS;
+
+		if (sub->event->type & WINFO_TYPE)
+		{
+			sub->updated = NO_UPDATE_TYPE;
+
+			if (unset_watchers_updated_winfo(&sub->pres_uri) < 0)
+			{
+				LM_ERR("resetting updated_winfo flags\n");
+				goto error;
+			}
+		}
+		else
+		{
+			if (sub->updated_winfo == UPDATED_TYPE
+				&& winfo_dialog_pending(&sub->pres_uri) > 0)
+			{
+					*updated = 1;
+					ret = 0;
+					goto done;
+			}
+		}
+	}
+	else /* Non-terminating dialog */
+	{
+		sub->updated = NO_UPDATE_TYPE;
+
+		if (sub->event->type & WINFO_TYPE) /* presence.winfo dialog */
+		{
+			if (sub->updated_winfo == NO_UPDATE_TYPE)
+			{
+				/* Partial notify if
+				   updated_winfo == NO_UPDATE_TYPE */
+				int len = 0;
+				char *version_str = int2str(sub->version,
+								&len);
+				if(version_str ==NULL)
+				{
+					LM_ERR("converting int to str\n");
+					goto error;
+				}
+
+				watchers = build_watchers_list(sub);
+				if(watchers == NULL)
+				{
+					LM_ERR("in build_watchers_list\n");
+					goto error;
+				}
+
+				nbody = create_winfo_xml(watchers,version_str,
+						sub->pres_uri,
+						sub->event->wipeer->name,
+						PARTIAL_STATE_FLAG);
+				if(nbody == NULL)
+				{
+					LM_ERR("in create_winfo_xml\n");
+					goto error;
+				}
+
+				if (unset_watchers_updated_winfo(
+							&sub->pres_uri) < 0)
+				{
+					LM_ERR("resetting updated_winfo "
+					       "flags\n");
+					goto error;
+				}
+			}
+			else	/* Full presence.winfo NOTIFY */
+				sub->updated_winfo = NO_UPDATE_TYPE;
+		}
+		else if (sub->event->type & PUBL_TYPE)
+		{
+			int tmp = watchers_awaiting_update(&sub->pres_uri,
+						sub->event);
+			if (tmp < 0)
+			{
+				LM_ERR("checking watchers\n");
+				goto error;
+			}
+			else if (tmp == 0)
+				attempt_delete_presentities = 1;
+		}
+		else if (!send_fast_notify)
+		{
+			ret = 0;
+			goto done;
+		}
+	}
+
+	if (notify(sub, NULL, nbody, 0) < 0)
+	{
+		LM_ERR("could not send notify\n");
+		goto error;
+	}
+
+	if (attempt_delete_presentities)
+	{
+		if (delete_offline_presentities(&sub->pres_uri, sub->event) < 0)
+		{
+			LM_ERR("deleting presentity\n");
+			goto error;
+		}
+	}
+
+	ret = 1;
+
+done:
+error:
+	free_notify_body(nbody, sub->event);
+	free_watcher_list(watchers);
+
+	return ret;
+}
+
+int process_dialogs(int round, int presence_winfo)
+{
+	db_key_t query_cols[3], result_cols[20], update_cols[4];
+	db_val_t query_vals[3], update_vals[4], *values, *dvalues;
+	db_op_t query_ops[2];
+	db_row_t *rows, *drows;
+	db1_res_t *dialog_list = NULL, *dialog = NULL;
+	int n_query_cols = 0, n_result_cols = 0, n_update_cols = 0;
+	int callid_col, to_tag_col, from_tag_col;
+	int pres_uri_col, tuser_col, tdomain_col, fuser_col, fdomain_col;
+	int wuser_col, wdomain_col, sockinfo_col, lcontact_col, contact_col;
+	int rroute_col, event_id_col, reason_col, event_col, lcseq_col;
+	int rcseq_col, status_col, version_col, updated_winfo_col, expires_col;
+	int i, notify_sent = 0, cached_updated_winfo, ret = -1;
+	subs_t sub;
+	str ev_sname, winfo = str_init("presence.winfo");
+	event_t parsed_event;
+	int now = (int)time(NULL);
+	int updated = 0;
+
+	if (++subset > (pres_waitn_time * pres_notifier_poll_rate) -1)
+		subset = 0;
+
+	query_cols[n_query_cols] = &str_updated_col;
+	query_vals[n_query_cols].type = DB1_INT;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.int_val = round;
+	query_ops[n_query_cols] = OP_EQ;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = winfo;
+	query_ops[n_query_cols] = presence_winfo ? OP_EQ : OP_NEQ;
+	n_query_cols++;
+
+	result_cols[callid_col = n_result_cols++] = &str_callid_col;
+	result_cols[to_tag_col = n_result_cols++] = &str_to_tag_col;
+	result_cols[from_tag_col = n_result_cols++] = &str_from_tag_col;
+	
+	update_cols[n_update_cols] = &str_updated_col;
+	update_vals[n_update_cols].type = DB1_INT;
+	update_vals[n_update_cols].nul = 0;
+	update_vals[n_update_cols].val.int_val = NO_UPDATE_TYPE;
+	n_update_cols++;
+
+	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+	{
+		LM_ERR("use table failed\n");
+		goto error;
+	}
+
+	if (pa_dbf.start_transaction)
+	{
+		if (pa_dbf.start_transaction(pa_db) < 0)
+		{
+			LM_ERR("in start_transaction\n");
+			goto error;
+		}
+	}
+
+	/* Step 1: Find active_watchers that require notification */
+	if (pa_dbf.query(pa_db, query_cols, query_ops, query_vals, result_cols,
+			 n_query_cols, n_result_cols, 0, &dialog_list) < 0)
+	{
+		LM_ERR("in sql query\n");
+		goto error;
+	}
+	if(dialog_list == NULL)
+	{
+		LM_ERR("bad result\n");
+		goto error;
+	}
+
+	if(dialog_list->n <= 0)
+		goto done;
+
+	/* Step 2: Update the records so they are not notified again */
+	if (pa_dbf.update(pa_db, query_cols, query_ops, query_vals, update_cols,
+			  update_vals, n_query_cols, n_update_cols) < 0)
+	{
+		LM_ERR("in sql update\n");
+		goto error;
+	}
+
+	if (pa_dbf.end_transaction)
+	{
+		if (pa_dbf.end_transaction(pa_db) < 0)
+		{
+			LM_ERR("in end_transaction\n");
+			goto error;
+		}
+	}
+
+	/* Step 3: Notify each watcher we found */
+	rows = RES_ROWS(dialog_list);
+	for (i = 0; i < RES_ROW_N(dialog_list); i++)
+	{
+		n_query_cols = 0;
+		n_result_cols = 0;
+		n_update_cols = 0;
+		memset(&sub, 0, sizeof(subs_t));
+		values = ROW_VALUES(&rows[i]);
+
+		EXTRACT_STRING(sub.callid, VAL_STRING(&values[callid_col]));
+		EXTRACT_STRING(sub.to_tag, VAL_STRING(&values[to_tag_col]));
+		EXTRACT_STRING(sub.from_tag, VAL_STRING(&values[from_tag_col]));
+
+		query_cols[n_query_cols] = &str_callid_col;
+		query_vals[n_query_cols].type = DB1_STR;
+		query_vals[n_query_cols].nul = 0;
+		query_vals[n_query_cols].val.str_val = sub.callid;
+		n_query_cols++;
+
+		query_cols[n_query_cols] = &str_to_tag_col;
+		query_vals[n_query_cols].type = DB1_STR;
+		query_vals[n_query_cols].nul = 0;
+		query_vals[n_query_cols].val.str_val = sub.to_tag;
+		n_query_cols++;
+
+		query_cols[n_query_cols] = &str_from_tag_col;
+		query_vals[n_query_cols].type = DB1_STR;
+		query_vals[n_query_cols].nul = 0;
+		query_vals[n_query_cols].val.str_val = sub.from_tag;
+		n_query_cols++;
+
+		result_cols[pres_uri_col = n_result_cols++] = &str_presentity_uri_col;
+		result_cols[tuser_col = n_result_cols++] = &str_to_user_col;
+		result_cols[tdomain_col = n_result_cols++] = &str_to_domain_col;
+		result_cols[fuser_col = n_result_cols++] = &str_from_user_col;
+		result_cols[fdomain_col = n_result_cols++] = &str_from_domain_col;
+		result_cols[wuser_col = n_result_cols++] = &str_watcher_username_col;
+		result_cols[wdomain_col = n_result_cols++] = &str_watcher_domain_col;
+		result_cols[sockinfo_col = n_result_cols++] = &str_socket_info_col;
+		result_cols[lcontact_col = n_result_cols++] = &str_local_contact_col;
+		result_cols[contact_col = n_result_cols++] = &str_contact_col;
+		result_cols[rroute_col = n_result_cols++] = &str_record_route_col;
+		result_cols[event_id_col = n_result_cols++] = &str_event_id_col;
+		result_cols[reason_col = n_result_cols++] = &str_reason_col;
+		result_cols[event_col = n_result_cols++] = &str_event_col;
+		result_cols[lcseq_col = n_result_cols++] = &str_local_cseq_col;
+		result_cols[rcseq_col = n_result_cols++] = &str_remote_cseq_col;
+		result_cols[status_col = n_result_cols++] = &str_status_col;
+		result_cols[version_col = n_result_cols++] = &str_version_col;
+		result_cols[updated_winfo_col = n_result_cols++] = &str_updated_winfo_col;
+		result_cols[expires_col = n_result_cols++] = &str_expires_col;
+
+		if (pa_dbf.start_transaction)
+		{
+			if (pa_dbf.start_transaction(pa_db) < 0)
+			{
+				LM_ERR("in start_transaction\n");
+				goto error;
+			}
+		}
+
+		if (pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols,
+				 n_query_cols, n_result_cols, 0, &dialog) < 0)
+		{
+			LM_ERR("in sql query\n");
+			goto error;
+		}
+		if (dialog == NULL || dialog->n <= 0)
+		{
+			LM_ERR("no records found\n");
+			goto error;
+		}
+		if (dialog->n > 1)
+		{
+			LM_ERR("multiple records found\n");
+			goto delete_dialog;
+		}
+
+		drows = RES_ROWS(dialog);
+		dvalues = ROW_VALUES(drows);
+
+		EXTRACT_STRING(sub.pres_uri, VAL_STRING(&dvalues[pres_uri_col]));
+		EXTRACT_STRING(sub.to_user, VAL_STRING(&dvalues[tuser_col]));
+		EXTRACT_STRING(sub.to_domain, VAL_STRING(&dvalues[tdomain_col]));
+		EXTRACT_STRING(sub.from_user, VAL_STRING(&dvalues[fuser_col]));
+		EXTRACT_STRING(sub.from_domain, VAL_STRING(&dvalues[fdomain_col]));
+		EXTRACT_STRING(sub.watcher_user, VAL_STRING(&dvalues[wuser_col]));
+		EXTRACT_STRING(sub.watcher_domain, VAL_STRING(&dvalues[wdomain_col]));
+		EXTRACT_STRING(sub.sockinfo_str, VAL_STRING(&dvalues[sockinfo_col]));
+		EXTRACT_STRING(sub.local_contact, VAL_STRING(&dvalues[lcontact_col]));
+		EXTRACT_STRING(sub.contact, VAL_STRING(&dvalues[contact_col]));
+		EXTRACT_STRING(sub.record_route, VAL_STRING(&dvalues[rroute_col]));
+		EXTRACT_STRING(sub.event_id, VAL_STRING(&dvalues[event_id_col]));
+		EXTRACT_STRING(sub.reason, VAL_STRING(&dvalues[reason_col]));
+		EXTRACT_STRING(ev_sname, VAL_STRING(&dvalues[event_col]));
+		sub.event = contains_event(&ev_sname, &parsed_event);
+		if (sub.event == NULL)
+		{
+			LM_ERR("event not found and set to NULL\n");
+			goto delete_dialog;
+		}
+
+		sub.local_cseq = VAL_INT(&dvalues[lcseq_col]) + 1;
+		sub.remote_cseq = VAL_INT(&dvalues[rcseq_col]);
+		sub.status = VAL_INT(&dvalues[status_col]);
+		sub.version = VAL_INT(&dvalues[version_col]) + 1;
+		cached_updated_winfo = sub.updated_winfo
+					= VAL_INT(&dvalues[updated_winfo_col]);
+		
+		if (VAL_INT(&dvalues[expires_col]) > now)
+			sub.expires = VAL_INT(&dvalues[expires_col]) - now;
+		else
+			sub.expires = 0;
+
+		if (sub.expires < expires_offset) sub.expires = 0;
+
+		sub.updated = round;
+
+		if ((notify_sent = notifier_notify(&sub, &updated)) < 0)
+		{
+			LM_ERR("sending NOTIFY request\n");
+
+			/* On send error remove the dialog and continue */
+			goto delete_dialog;
+		}
+
+		if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+		{
+			LM_ERR("use table failed\n");
+			goto error;
+		}
+
+		if ((sub.expires == 0 || sub.status == TERMINATED_STATUS)
+		    && notify_sent)
+		{
+			if (pa_dbf.delete(pa_db, query_cols, 0, query_vals,
+						n_query_cols) < 0)
+			{	
+				LM_ERR("in sql delete");
+				goto error;
+			}
+		}
+		else
+		{
+			if (sub.updated_winfo != cached_updated_winfo)
+			{
+				update_cols[n_update_cols] = &str_updated_winfo_col;
+				update_vals[n_update_cols].type = DB1_INT;
+				update_vals[n_update_cols].nul = 0;
+				update_vals[n_update_cols].val.int_val = sub.updated_winfo;
+				n_update_cols++;
+			}
+
+			if (updated)
+			{
+				update_cols[n_update_cols] = &str_updated_col;
+				update_vals[n_update_cols].type = DB1_INT;
+				update_vals[n_update_cols].nul = 0;
+				update_vals[n_update_cols].val.int_val = round;
+				n_update_cols++;
+			}
+
+			if (notify_sent)
+			{
+				update_cols[n_update_cols] = &str_local_cseq_col;
+				update_vals[n_update_cols].type = DB1_INT;
+				update_vals[n_update_cols].nul = 0;
+				update_vals[n_update_cols].val.int_val = sub.local_cseq;
+				n_update_cols++;
+
+				update_cols[n_update_cols] = &str_version_col;
+				update_vals[n_update_cols].type = DB1_INT;
+				update_vals[n_update_cols].nul = 0;
+				update_vals[n_update_cols].val.int_val = sub.version;
+				n_update_cols++;
+			}
+
+			if (n_update_cols > 0)
+			{
+				if (pa_dbf.update(pa_db,
+						  query_cols,
+						  0,
+						  query_vals,
+						  update_cols,
+						  update_vals,
+						  n_query_cols,
+						  n_update_cols) < 0)
+				{
+					LM_ERR("in sql update\n");
+					goto error;
+				}
+			}
+		}
+
+		goto next_dialog;
+
+delete_dialog:
+		if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
+		{
+			LM_ERR("use table failed\n");
+			goto error;
+		}
+
+		if (pa_dbf.delete(pa_db, query_cols, 0, query_vals,
+					n_query_cols) < 0)
+		{	
+			LM_ERR("in sql delete");
+			goto error;
+		}
+
+next_dialog:
+		if (pa_dbf.end_transaction)
+		{
+			if (pa_dbf.end_transaction(pa_db) < 0)
+			{
+				LM_ERR("in end_transaction\n");
+				goto error;
+			}
+		}
+
+		pa_dbf.free_result(pa_db, dialog);
+		dialog = NULL;
+	}
+
+done:
+	ret = 0;
+error:
+	if (dialog_list) pa_dbf.free_result(pa_db, dialog_list);
+	if (dialog) pa_dbf.free_result(pa_db, dialog);
+
+	if (pa_dbf.abort_transaction)
+	{
+		if (pa_dbf.abort_transaction(pa_db) < 0)
+			LM_ERR("in abort_transaction\n");
+	}
+
+	return ret;
+}
+
+void pres_timer_send_notify(unsigned int ticks, void *param)
+{
+	int process_num = *((int *) param);
+	int round = subset + (pres_waitn_time * pres_notifier_poll_rate
+				* process_num);
+
+	if (process_dialogs(round, 0) < 0)
+	{
+		LM_ERR("Handling non presence.winfo dialogs\n");
+		return;
+	}
+	if (process_dialogs(round, 1) < 0)
+	{
+		LM_ERR("Handling presence.winfo dialogs\n");
+		return;
+	}
+}

+ 4 - 0
modules_k/presence/notify.h

@@ -110,6 +110,9 @@ int query_db_notify(str* pres_uri,pres_ev_t* event, subs_t* watcher_subs );
 
 
 int publ_notify(presentity_t* p, str pres_uri, str* body, str* offline_etag,
 int publ_notify(presentity_t* p, str pres_uri, str* body, str* offline_etag,
 		str* rules_doc);
 		str* rules_doc);
+int publ_notify_notifier(str pres_uri, pres_ev_t *event);
+int set_updated(subs_t *sub);
+int set_wipeer_subs_updated(str *pres_uri, pres_ev_t *event, int full);
 
 
 int notify(subs_t* subs, subs_t* watcher_subs, str* n_body,int force_null_body);
 int notify(subs_t* subs, subs_t* watcher_subs, str* n_body,int force_null_body);
 
 
@@ -120,4 +123,5 @@ char* get_status_str(int flag);
 
 
 str *get_p_notify_body(str pres_uri, pres_ev_t *event, str *etag, str *contact);
 str *get_p_notify_body(str pres_uri, pres_ev_t *event, str *etag, str *contact);
 void free_notify_body(str *body, pres_ev_t *ev);
 void free_notify_body(str *body, pres_ev_t *ev);
+void pres_timer_send_notify(unsigned int ticks, void *param);
 #endif
 #endif

+ 92 - 24
modules_k/presence/presence.c

@@ -72,6 +72,7 @@
 #include "bind_presence.h"
 #include "bind_presence.h"
 #include "notify.h"
 #include "notify.h"
 #include "../../mod_fix.h"
 #include "../../mod_fix.h"
+#include "../../timer_proc.h"
 
 
 MODULE_VERSION
 MODULE_VERSION
 
 
@@ -145,8 +146,13 @@ int sphere_enable= 0;
 int timeout_rm_subs = 1;
 int timeout_rm_subs = 1;
 int send_fast_notify = 1;
 int send_fast_notify = 1;
 int publ_cache_enabled = 1;
 int publ_cache_enabled = 1;
+int pres_waitn_time = 5;
+int pres_notifier_poll_rate = 10;
+int pres_notifier_processes = 1;
 int pres_integrated_xcap_server = 0;
 int pres_integrated_xcap_server = 0;
 
 
+int *pres_notifier_id = NULL;
+
 int phtable_size= 9;
 int phtable_size= 9;
 phtable_t* pres_htable=NULL;
 phtable_t* pres_htable=NULL;
 
 
@@ -182,6 +188,9 @@ static param_export_t params[]={
 	{ "xcap_table",             STR_PARAM, &pres_xcap_table.s},
 	{ "xcap_table",             STR_PARAM, &pres_xcap_table.s},
 	{ "clean_period",           INT_PARAM, &clean_period },
 	{ "clean_period",           INT_PARAM, &clean_period },
 	{ "db_update_period",       INT_PARAM, &db_update_period },
 	{ "db_update_period",       INT_PARAM, &db_update_period },
+	{ "waitn_time",             INT_PARAM, &pres_waitn_time },
+	{ "notifier_poll_rate",     INT_PARAM, &pres_notifier_poll_rate },
+	{ "notifier_processes",     INT_PARAM, &pres_notifier_processes },
 	{ "to_tag_pref",            STR_PARAM, &to_tag_pref },
 	{ "to_tag_pref",            STR_PARAM, &to_tag_pref },
 	{ "expires_offset",         INT_PARAM, &expires_offset },
 	{ "expires_offset",         INT_PARAM, &expires_offset },
 	{ "max_expires",            INT_PARAM, &max_expires },
 	{ "max_expires",            INT_PARAM, &max_expires },
@@ -416,6 +425,26 @@ static int mod_init(void)
 	if(db_update_period>0)
 	if(db_update_period>0)
 		register_timer(timer_db_update, 0, db_update_period);
 		register_timer(timer_db_update, 0, db_update_period);
 
 
+	if (pres_waitn_time <= 0)
+		pres_waitn_time = 5;
+
+	if (pres_notifier_poll_rate <= 0)
+		pres_notifier_poll_rate = 10;
+
+	if (pres_notifier_processes < 0 || subs_dbmode != DB_ONLY)
+		pres_notifier_processes = 0;
+
+	if (pres_notifier_processes > 0)
+	{
+		if ((pres_notifier_id = shm_malloc(sizeof(int) * pres_notifier_processes)) == NULL)
+		{
+			LM_ERR("allocating shared memory\n");
+			return -1;
+		}
+
+		register_basic_timers(pres_notifier_processes);
+	}
+
 	pa_dbf.close(pa_db);
 	pa_dbf.close(pa_db);
 	pa_db = NULL;
 	pa_db = NULL;
 
 
@@ -433,8 +462,31 @@ static int mod_init(void)
  */
  */
 static int child_init(int rank)
 static int child_init(int rank)
 {
 {
-	if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
-		return 0; /* do nothing for the main process */
+	if (rank==PROC_INIT || rank==PROC_TCP_MAIN)
+		return 0;
+
+	if (rank == PROC_MAIN)
+	{
+		int i;
+
+		for (i = 0; i < pres_notifier_processes; i++)
+		{
+			char tmp[21];
+			snprintf(tmp, 21, "PRESENCE NOTIFIER %d", i);
+			pres_notifier_id[i] = i;
+
+			if (fork_basic_utimer(PROC_TIMER, tmp, 1,
+						pres_timer_send_notify,
+						&pres_notifier_id[i],
+						1000000/pres_notifier_poll_rate) < 0)
+			{
+				LM_ERR("Failed to start PRESENCE NOTIFIER %d\n", i);
+				return -1;
+			}
+		}
+
+		return 0;
+	}
 
 
 	pid = my_pid();
 	pid = my_pid();
 	
 	
@@ -593,6 +645,9 @@ static void destroy(void)
 	if(pres_xcap_db && pres_xcap_dbf.close)
 	if(pres_xcap_db && pres_xcap_dbf.close)
 		pres_xcap_dbf.close(pres_xcap_db);
 		pres_xcap_dbf.close(pres_xcap_db);
 
 
+	if (pres_notifier_id != NULL)
+		shm_free(pres_notifier_id);
+
 	destroy_evlist();
 	destroy_evlist();
 }
 }
 
 
@@ -1005,14 +1060,14 @@ int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc)
 		goto done;
 		goto done;
 	}
 	}
 
 
-    LM_DBG("found %d record-uri in watchers_table\n", result->n);
+	LM_DBG("found %d record-uri in watchers_table\n", result->n);
 	hash_code= core_hash(&pres_uri, &ev->name, shtable_size);
 	hash_code= core_hash(&pres_uri, &ev->name, shtable_size);
 	subs.db_flag= hash_code;
 	subs.db_flag= hash_code;
 
 
-    /*must do a copy as sphere_check requires database queries */
+	/*must do a copy as sphere_check requires database queries */
 	if(sphere_enable)
 	if(sphere_enable)
 	{
 	{
-        n= result->n;
+        	n= result->n;
 		ws_list= (ws_t*)pkg_malloc(n * sizeof(ws_t));
 		ws_list= (ws_t*)pkg_malloc(n * sizeof(ws_t));
 		if(ws_list== NULL)
 		if(ws_list== NULL)
 		{
 		{
@@ -1132,35 +1187,38 @@ int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc)
 			LM_ERR("failed to update watcher status\n");
 			LM_ERR("failed to update watcher status\n");
 			goto done;
 			goto done;
 		}
 		}
-    }
+	}
 
 
 	pa_dbf.free_result(pa_db, result);
 	pa_dbf.free_result(pa_db, result);
 	result= NULL;
 	result= NULL;
 
 
 send_notify:
 send_notify:
 
 
-	s= subs_array;
-
-	while(s)
+	if (pres_notifier_processes == 0)
 	{
 	{
-		if(notify(s, NULL, NULL, 0)< 0)
-		{
-			LM_ERR( "sending Notify request\n");
-			goto done;
-		}
+		s= subs_array;
 
 
-		/* delete from database also */
-		if(s->status== TERMINATED_STATUS)
+		while(s)
 		{
 		{
-			if(pres_db_delete_status(s)<0)
+			if(notify(s, NULL, NULL, 0)< 0)
 			{
 			{
-				err_ret= -1;
-				LM_ERR("failed to delete terminated dialog from database\n");
+				LM_ERR( "sending Notify request\n");
 				goto done;
 				goto done;
 			}
 			}
-		}
 
 
-		s= s->next;
+			/* delete from database also */
+			if(s->status== TERMINATED_STATUS)
+			{
+				if(pres_db_delete_status(s)<0)
+				{
+					LM_ERR("failed to delete terminated "
+						"dialog from database\n");
+					goto done;
+				}
+			}
+
+			s= s->next;
+		}
 	}
 	}
 
 
 	free_subs_list(subs_array, PKG_MEM_TYPE, 0);
 	free_subs_list(subs_array, PKG_MEM_TYPE, 0);
@@ -1191,8 +1249,8 @@ done:
 
 
 static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 {
 {
-	db_key_t query_cols[5], db_cols[2];
-	db_val_t query_vals[5], db_vals[2];
+	db_key_t query_cols[5], db_cols[3];
+	db_val_t query_vals[5], db_vals[3];
 	db_key_t result_cols[24];
 	db_key_t result_cols[24];
 	int n_query_cols=0, n_result_cols=0, n_update_cols=0;
 	int n_query_cols=0, n_result_cols=0, n_update_cols=0;
 	int event_col, pres_uri_col, watcher_user_col, watcher_domain_col;
 	int event_col, pres_uri_col, watcher_user_col, watcher_domain_col;
@@ -1384,7 +1442,7 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 
 
 	pa_dbf.free_result(pa_db, result);
 	pa_dbf.free_result(pa_db, result);
 
 
-	if (subs->status == TERMINATED_STATUS)
+	if (pres_notifier_processes == 0 && subs->status == TERMINATED_STATUS)
 	{
 	{
 		/* delete the records */
 		/* delete the records */
 		if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols)< 0)
 		if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols)< 0)
@@ -1409,6 +1467,16 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 	db_vals[n_update_cols].val.str_val= subs->reason;
 	db_vals[n_update_cols].val.str_val= subs->reason;
 	n_update_cols++;
 	n_update_cols++;
 
 
+	db_cols[n_update_cols] = &str_updated_col; 
+	db_vals[n_update_cols].type = DB1_INT;
+	db_vals[n_update_cols].nul = 0; 
+	db_vals[n_update_cols].val.int_val = 
+		core_hash(&subs->callid, &subs->from_tag,
+			  (pres_waitn_time * pres_notifier_poll_rate
+					* pres_notifier_processes) - 1);
+	n_update_cols++;
+
+
 	if(pa_dbf.update(pa_db, query_cols, 0, query_vals,
 	if(pa_dbf.update(pa_db, query_cols, 0, query_vals,
 				db_cols,db_vals,n_query_cols,n_update_cols) < 0)
 				db_cols,db_vals,n_query_cols,n_update_cols) < 0)
 	{
 	{

+ 7 - 0
modules_k/presence/presence.h

@@ -52,6 +52,9 @@
 /** subscriptions are stored only in database */
 /** subscriptions are stored only in database */
 #define DB_ONLY          3
 #define DB_ONLY          3
 
 
+#define NO_UPDATE_TYPE	-1
+#define UPDATED_TYPE	1
+
 /** TM bind */
 /** TM bind */
 extern struct tm_binds tmb;
 extern struct tm_binds tmb;
 
 
@@ -84,6 +87,10 @@ extern int send_fast_notify;
 extern int shtable_size;
 extern int shtable_size;
 extern shtable_t subs_htable;
 extern shtable_t subs_htable;
 
 
+extern int pres_waitn_time;
+extern int pres_notifier_poll_rate;
+extern int pres_notifier_processes;
+
 extern int phtable_size;
 extern int phtable_size;
 extern phtable_t* pres_htable;
 extern phtable_t* pres_htable;
 
 

+ 229 - 17
modules_k/presence/presentity.c

@@ -54,6 +54,8 @@ xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name,
 static str pu_200_rpl  = str_init("OK");
 static str pu_200_rpl  = str_init("OK");
 static str pu_412_rpl  = str_init("Conditional request failed");
 static str pu_412_rpl  = str_init("Conditional request failed");
 
 
+static str str_offline_etag_val = str_init("*#-OFFLINE-#*");
+
 extern int pres_fetch_rows;
 extern int pres_fetch_rows;
 
 
 #define ETAG_LEN  128
 #define ETAG_LEN  128
@@ -290,9 +292,10 @@ int update_presentity(struct sip_msg* msg, presentity_t* presentity, str* body,
 	int affected_rows = 0;
 	int affected_rows = 0;
 	int ret = -1;
 	int ret = -1;
 	int db_record_exists = 0;
 	int db_record_exists = 0;
+	int num_watchers = 0;
 
 
 	if (sent_reply) *sent_reply= 0;
 	if (sent_reply) *sent_reply= 0;
-	if(presentity->event->req_auth)
+	if(pres_notifier_processes == 0 && presentity->event->req_auth)
 	{
 	{
 		/* get rules_document */
 		/* get rules_document */
 		if(presentity->event->get_rules_doc(&presentity->user,
 		if(presentity->event->get_rules_doc(&presentity->user,
@@ -531,26 +534,50 @@ after_dialog_check:
 				goto error;
 				goto error;
 			}
 			}
 			if (sent_reply) *sent_reply= 1;
 			if (sent_reply) *sent_reply= 1;
-			if( publ_notify( presentity, pres_uri, body, &presentity->etag, rules_doc)< 0 )
+
+			if (pres_notifier_processes > 0)
 			{
 			{
-				LM_ERR("while sending notify\n");
-				goto error;
+				if ((num_watchers = publ_notify_notifier(pres_uri, presentity->event)) < 0)
+				{
+					LM_ERR("updating watcher records\n");
+					goto error;
+				}
+				if (num_watchers > 0)
+				{
+					if (mark_presentity_for_delete(presentity) < 0)
+					{
+						LM_ERR("Marking presentities\n");
+						goto error;
+					}
+				}
 			}
 			}
-
-			if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
+			else
 			{
 			{
-				LM_ERR("unsuccessful use table sql operation\n");
-				goto error;
+				if( publ_notify( presentity, pres_uri, body, &presentity->etag, rules_doc)< 0 )
+				{
+					LM_ERR("while sending notify\n");
+					goto error;
+				}
 			}
 			}
 
 
-			LM_DBG("expires =0 -> deleting from database\n");
-			if(pa_dbf.delete(pa_db,query_cols,0,query_vals,n_query_cols)<0)
+			if (pres_notifier_processes == 0 || num_watchers == 0)
 			{
 			{
-				LM_ERR("unsuccessful sql delete operation");
-				goto error;
-			}
+				if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
+				{
+					LM_ERR("unsuccessful use table sql operation\n");
+					goto error;
+				}
 
 
-			LM_DBG("deleted from db %.*s\n", presentity->user.len, presentity->user.s);
+				LM_DBG("expires =0 -> deleting from database\n");
+
+				if(pa_dbf.delete(pa_db,query_cols,0,query_vals,n_query_cols)<0)
+				{
+					LM_ERR("unsuccessful sql delete operation");
+					goto error;
+				}
+
+				LM_DBG("deleted from db %.*s\n", presentity->user.len, presentity->user.s);
+			}
 
 
 			/* delete from hash table */
 			/* delete from hash table */
 			if( publ_cache_enabled &&
 			if( publ_cache_enabled &&
@@ -730,10 +757,21 @@ after_dialog_check:
 send_notify:
 send_notify:
 
 
 	/* send notify with presence information */
 	/* send notify with presence information */
-	if (publ_notify(presentity, pres_uri, body, NULL, rules_doc)<0)
+	if (pres_notifier_processes == 0 || num_watchers == 0)
 	{
 	{
-		LM_ERR("while sending Notify requests to watchers\n");
-		goto error;
+		if (publ_notify_notifier(pres_uri, presentity->event) < 0)
+		{
+			LM_ERR("updating watcher records\n");
+			goto error;
+		}
+	}
+	else
+	{
+		if( publ_notify( presentity, pres_uri, body, NULL, rules_doc)< 0 )
+		{
+			LM_ERR("while sending notify\n");
+			goto error;
+		}
 	}
 	}
 
 
 done:
 done:
@@ -1085,3 +1123,177 @@ error:
 
 
 }
 }
 
 
+int mark_presentity_for_delete(presentity_t *pres)
+{
+	db_key_t query_cols[4], result_cols[1], update_cols[2];
+	db_val_t query_vals[4], update_vals[2], *value;
+	db_row_t *row;
+	db1_res_t *result = NULL;
+	int n_query_cols = 0, n_update_cols = 0;
+	int ret = -1;
+	str *cur_body, *new_body = NULL;
+
+	if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
+	{
+		LM_ERR("unsuccessful use table sql operation\n");
+		goto error;
+	}
+
+	query_cols[n_query_cols] = &str_username_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = pres->user;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_domain_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = pres->domain;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = pres->event->name;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_etag_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = pres->etag;
+	n_query_cols++;
+
+	if (pres->event->agg_nbody == NULL)
+	{
+		/* Nothing clever to do here... just delete */
+		if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols) < 0)
+		{
+			LM_ERR("unsuccessful sql delete operation");
+			goto error;
+		}
+
+		ret = 0;
+		goto done;
+	}
+
+	result_cols[0] = &str_body_col;
+
+	if (pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols,
+				n_query_cols, 1, 0, &result) < 0)
+	{
+		LM_ERR("query failed\n");
+		goto error;
+	}
+
+	if (result == NULL)
+	{
+		LM_ERR("bad result\n");
+		goto error;
+	}
+
+	if (RES_ROW_N(result) != 1)
+	{
+		LM_ERR("Found %d presentities - expected 1\n", RES_ROW_N(result));
+		goto error;
+	}
+
+	row = RES_ROWS(result);
+	value = ROW_VALUES(row);
+
+	if ((cur_body = (str *) pkg_malloc(sizeof(str))) == NULL)
+	{
+		LM_ERR("allocating pkg memory\n");
+		goto error;
+	}
+	cur_body->s = (char *) value->val.string_val;
+	cur_body->len = strlen (cur_body->s);
+	if ((new_body = pres->event->agg_nbody(&pres->user, &pres->domain,
+						&cur_body, 1, 0)) == NULL)
+	{
+		LM_ERR("preparing body\n");
+		goto error;
+	}
+
+	update_cols[n_update_cols] = &str_etag_col;
+	update_vals[n_update_cols].type = DB1_STR;
+	update_vals[n_update_cols].nul = 0;
+	update_vals[n_update_cols].val.str_val = str_offline_etag_val;
+	n_update_cols++;
+
+	update_cols[n_update_cols] = &str_body_col;
+	update_vals[n_update_cols].type = DB1_STR;
+	update_vals[n_update_cols].nul = 0;
+	update_vals[n_update_cols].val.str_val.s = new_body->s;
+	update_vals[n_update_cols].val.str_val.len = new_body->len;
+	n_update_cols++;
+
+	if(pa_dbf.update(pa_db, query_cols, 0, query_vals, update_cols,
+			 update_vals, n_query_cols, n_update_cols) < 0)
+	{
+		LM_ERR("unsuccessful sql update operation");
+		goto error;
+	}
+
+	ret = 1;
+done:
+error:
+	free_notify_body(new_body, pres->event);
+	if (cur_body) pkg_free(cur_body);
+	if (result) pa_dbf.free_result(pa_db, result);
+	return ret;
+}
+
+int delete_offline_presentities(str *pres_uri, pres_ev_t *event)
+{
+	db_key_t query_cols[4];
+	db_val_t query_vals[4];
+	int n_query_cols = 0;
+	struct sip_uri uri;
+
+	if (parse_uri(pres_uri->s, pres_uri->len, &uri) < 0)
+	{
+		LM_ERR("failed to parse presentity uri\n");
+		goto error;
+	}
+
+	query_cols[n_query_cols] = &str_username_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = uri.user;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_domain_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = uri.host;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_event_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = event->name;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_etag_col;
+	query_vals[n_query_cols].type = DB1_STR;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.str_val = str_offline_etag_val;
+	n_query_cols++;
+
+	if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
+	{
+		LM_ERR("unsuccessful use table sql operation\n");
+		goto error;
+	}
+
+	if (pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols) < 0)
+	{
+		LM_ERR("unsuccessful sql delete operation");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	return -1;
+}

+ 2 - 0
modules_k/presence/presentity.h

@@ -71,6 +71,8 @@ char* extract_sphere(str body);
 char* get_sphere(str* pres_uri);
 char* get_sphere(str* pres_uri);
 typedef char* (*pres_get_sphere_t)(str* pres_uri);
 typedef char* (*pres_get_sphere_t)(str* pres_uri);
 
 
+int mark_presentity_for_delete(presentity_t *pres);
+int delete_offline_presentities(str *pres_uri, pres_ev_t *event);
 
 
 #endif
 #endif
 
 

+ 51 - 19
modules_k/presence/publish.c

@@ -85,7 +85,7 @@ void msg_presentity_clean(unsigned int ticks,void *param)
 	str user, domain, etag, event;
 	str user, domain, etag, event;
 	int n_result_cols= 0;
 	int n_result_cols= 0;
 	str* rules_doc= NULL;
 	str* rules_doc= NULL;
-
+	int num_watchers = 0;
 
 
 	LM_DBG("cleaning expired presentity information\n");
 	LM_DBG("cleaning expired presentity information\n");
 	if (pa_dbf.use_table(pa_db, &presentity_table) < 0) 
 	if (pa_dbf.use_table(pa_db, &presentity_table) < 0) 
@@ -214,25 +214,48 @@ void msg_presentity_clean(unsigned int ticks,void *param)
 	{
 	{
 		LM_DBG("found expired publish for [user]=%.*s  [domanin]=%.*s\n",
 		LM_DBG("found expired publish for [user]=%.*s  [domanin]=%.*s\n",
 			p[i].p->user.len,p[i].p->user.s, p[i].p->domain.len, p[i].p->domain.s);
 			p[i].p->user.len,p[i].p->user.s, p[i].p->domain.len, p[i].p->domain.s);
-		
-		rules_doc= NULL;
-		
-		if(p[i].p->event->get_rules_doc && 
-		p[i].p->event->get_rules_doc(&p[i].p->user, &p[i].p->domain, &rules_doc)< 0)
-		{
-			LM_ERR("getting rules doc\n");
-			goto error;
-		}
-		if(publ_notify( p[i].p, p[i].uri, NULL, &p[i].p->etag, rules_doc)< 0)
+
+		if (pres_notifier_processes > 0)
 		{
 		{
-			LM_ERR("sending Notify request\n");
-			goto error;
+			if ((num_watchers = publ_notify_notifier(p[i].uri,
+							p[i].p->event)) < 0)
+			{
+				LM_ERR("Updating watcher records\n");
+				goto error;
+			}
+			if (num_watchers > 0)
+			{
+				if (mark_presentity_for_delete(p[i].p) < 0)
+				{
+					LM_ERR("Marking presentities\n");
+					goto error;
+				}
+			}
 		}
 		}
-		if(rules_doc)
+		else
 		{
 		{
-			if(rules_doc->s)
-				pkg_free(rules_doc->s);
-			pkg_free(rules_doc);
+			rules_doc= NULL;
+		
+			if(p[i].p->event->get_rules_doc && 
+				p[i].p->event->get_rules_doc(&p[i].p->user,
+								&p[i].p->domain,
+								&rules_doc)< 0)
+			{
+				LM_ERR("getting rules doc\n");
+				goto error;
+			}
+			if(publ_notify(p[i].p, p[i].uri, NULL, &p[i].p->etag,
+					rules_doc)< 0)
+			{
+				LM_ERR("sending Notify request\n");
+				goto error;
+			}
+			if(rules_doc)
+			{
+				if(rules_doc->s)
+					pkg_free(rules_doc->s);
+				pkg_free(rules_doc);
+			}
 		}
 		}
 		rules_doc= NULL;
 		rules_doc= NULL;
 		pkg_free(p[i].p);
 		pkg_free(p[i].p);
@@ -247,8 +270,11 @@ void msg_presentity_clean(unsigned int ticks,void *param)
 	}
 	}
 
 
 delete_pres:
 delete_pres:
-	if (pa_dbf.delete(pa_db, db_keys, db_ops, db_vals, 2) < 0) 
-		LM_ERR("failed to delete expired records from DB\n");
+	if (pres_notifier_processes <= 0 || num_watchers == 0)
+	{
+		if (pa_dbf.delete(pa_db, db_keys, db_ops, db_vals, 2) < 0) 
+			LM_ERR("failed to delete expired records from DB\n");
+	}
 
 
 	return;
 	return;
 
 
@@ -579,6 +605,12 @@ static int fetch_presentity(str furi, str *presentity)
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if (result == NULL)
+	{
+		LM_ERR("bad result\n");
+		return -1;
+	}
+
 	if (result->n <=0)
 	if (result->n <=0)
 	{
 	{
 		pres_xcap_dbf.free_result(pres_xcap_db, result);
 		pres_xcap_dbf.free_result(pres_xcap_db, result);

+ 182 - 84
modules_k/presence/subscribe.c

@@ -338,8 +338,8 @@ int insert_subs_db(subs_t* s, int type)
 	query_vals[status_col].val.int_val= s->status;
 	query_vals[status_col].val.int_val= s->status;
 	query_vals[reason_col].val.str_val= s->reason;
 	query_vals[reason_col].val.str_val= s->reason;
 	query_vals[socket_info_col].val.str_val= s->sockinfo_str;
 	query_vals[socket_info_col].val.str_val= s->sockinfo_str;
-	query_vals[updated_col].val.int_val = -1;
-	query_vals[updated_winfo_col].val.int_val = -1;
+	query_vals[updated_col].val.int_val = s->updated;
+	query_vals[updated_winfo_col].val.int_val = s->updated_winfo;
 
 
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
 	{
 	{
@@ -358,8 +358,8 @@ int insert_subs_db(subs_t* s, int type)
 
 
 int update_subs_db(subs_t* subs, int type)
 int update_subs_db(subs_t* subs, int type)
 {
 {
-	db_key_t query_cols[3], update_keys[6];
-	db_val_t query_vals[3], update_vals[6];
+	db_key_t query_cols[3], update_keys[8];
+	db_val_t query_vals[3], update_vals[8];
 	int n_update_cols= 0;
 	int n_update_cols= 0;
 	int n_query_cols = 0;
 	int n_query_cols = 0;
 
 
@@ -394,6 +394,18 @@ int update_subs_db(subs_t* subs, int type)
 		update_vals[n_update_cols].nul = 0;
 		update_vals[n_update_cols].nul = 0;
 		update_vals[n_update_cols].val.int_val = subs->remote_cseq; 
 		update_vals[n_update_cols].val.int_val = subs->remote_cseq; 
 		n_update_cols++;
 		n_update_cols++;
+
+		update_keys[n_update_cols] = &str_updated_col;
+		update_vals[n_update_cols].type = DB1_INT;
+		update_vals[n_update_cols].nul = 0;
+		update_vals[n_update_cols].val.int_val = subs->updated;
+		n_update_cols++;
+
+		update_keys[n_update_cols] = &str_updated_winfo_col;
+		update_vals[n_update_cols].type = DB1_INT;
+		update_vals[n_update_cols].nul = 0;
+		update_vals[n_update_cols].val.int_val = subs->updated_winfo;
+		n_update_cols++;
 	}
 	}
 	if(type & LOCAL_TYPE)
 	if(type & LOCAL_TYPE)
 	{
 	{
@@ -421,7 +433,7 @@ int update_subs_db(subs_t* subs, int type)
 	update_vals[n_update_cols].nul = 0;
 	update_vals[n_update_cols].nul = 0;
 	update_vals[n_update_cols].val.str_val = subs->reason;
 	update_vals[n_update_cols].val.str_val = subs->reason;
 	n_update_cols++;
 	n_update_cols++;
-	
+
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
 	{
 	{
 		LM_ERR("in use table sql operation\n");	
 		LM_ERR("in use table sql operation\n");	
@@ -452,6 +464,62 @@ void delete_subs(str* pres_uri, str* ev_name, str* to_tag,
 		LM_ERR("Failed to delete subscription from database\n");
 		LM_ERR("Failed to delete subscription from database\n");
 }
 }
 
 
+int update_subscription_notifier(struct sip_msg* msg, subs_t* subs,
+		int to_tag_gen, int* sent_reply)
+{
+	*sent_reply= 0;
+
+	/* Set the notifier/update fields for the subscription */
+	subs->updated = core_hash(&subs->callid, &subs->from_tag,
+				(pres_waitn_time * pres_notifier_poll_rate
+					* pres_notifier_processes) - 1);
+	if (subs->event->type & WINFO_TYPE)
+		subs->updated_winfo = UPDATED_TYPE;
+	else if (subs->event->wipeer)
+	{
+		if (set_wipeer_subs_updated(&subs->pres_uri,
+						subs->event->wipeer,
+						subs->expires == 0) < 0)
+		{
+			LM_ERR("failed to update database record(s)\n");
+			goto error;
+		}
+		subs->updated_winfo = UPDATED_TYPE;
+	}
+
+	printf_subs(subs);
+
+	if (to_tag_gen == 0)
+	{
+		if (update_subs_db(subs, REMOTE_TYPE) < 0)
+		{
+			LM_ERR("updating subscription in database table\n");
+			goto error;
+		}
+	}
+	else
+	{
+		subs->version = 1;
+		if (insert_subs_db(subs, REMOTE_TYPE) < 0)
+		{
+			LM_ERR("failed to insert new record in database\n");
+			goto error;
+		}
+	}
+
+	if(send_2XX_reply(msg, subs->event->type & PUBL_TYPE ? 202 : 200,
+				subs->expires, &subs->local_contact) < 0)
+	{
+		LM_ERR("sending 202 OK\n");
+		goto error;
+	}
+	*sent_reply= 1;
+
+	return 1;
+
+error:
+	return -1;
+}
 
 
 int update_subscription(struct sip_msg* msg, subs_t* subs, int to_tag_gen,
 int update_subscription(struct sip_msg* msg, subs_t* subs, int to_tag_gen,
 		int* sent_reply)
 		int* sent_reply)
@@ -817,6 +885,9 @@ int handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_domain)
 	/* if dialog initiation Subscribe - get subscription state */
 	/* if dialog initiation Subscribe - get subscription state */
 	if(to_tag_gen)
 	if(to_tag_gen)
 	{
 	{
+		subs.updated = NO_UPDATE_TYPE;
+		subs.updated_winfo = NO_UPDATE_TYPE;
+
 		if(!event->req_auth) 
 		if(!event->req_auth) 
 			subs.status = ACTIVE_STATUS;
 			subs.status = ACTIVE_STATUS;
 		else
 		else
@@ -880,7 +951,16 @@ int handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_domain)
 	LM_DBG("subscription status= %s - %s\n", get_status_str(subs.status), 
 	LM_DBG("subscription status= %s - %s\n", get_status_str(subs.status), 
             found==0?"inserted":"found in watcher table");
             found==0?"inserted":"found in watcher table");
 	
 	
-	if(update_subscription(msg, &subs, to_tag_gen, &sent_reply) <0)
+	if (pres_notifier_processes > 0)
+	{
+		if (update_subscription_notifier(msg, &subs, to_tag_gen,
+							&sent_reply) < 0)
+		{
+			LM_ERR("in update_subscription_notifier\n");
+			goto error;
+		}
+	}
+	else if (update_subscription(msg, &subs, to_tag_gen, &sent_reply) <0)
 	{	
 	{	
 		LM_ERR("in update_subscription\n");
 		LM_ERR("in update_subscription\n");
 		goto error;
 		goto error;
@@ -1326,7 +1406,7 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 {
 {
 	db_key_t query_cols[3];
 	db_key_t query_cols[3];
 	db_val_t query_vals[3];
 	db_val_t query_vals[3];
-	db_key_t result_cols[7];
+	db_key_t result_cols[9];
 	db1_res_t *result= NULL;
 	db1_res_t *result= NULL;
 	db_row_t *row ;	
 	db_row_t *row ;	
 	db_val_t *row_vals ;
 	db_val_t *row_vals ;
@@ -1334,6 +1414,7 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	int n_result_cols = 0;
 	int n_result_cols = 0;
 	int remote_cseq_col= 0, local_cseq_col= 0, status_col, reason_col;
 	int remote_cseq_col= 0, local_cseq_col= 0, status_col, reason_col;
 	int record_route_col, version_col, pres_uri_col;
 	int record_route_col, version_col, pres_uri_col;
+	int updated_col, updated_winfo_col;
 	unsigned int remote_cseq;
 	unsigned int remote_cseq;
 	str pres_uri, record_route;
 	str pres_uri, record_route;
 	str reason;
 	str reason;
@@ -1363,6 +1444,8 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	result_cols[reason_col=n_result_cols++] = &str_reason_col;
 	result_cols[reason_col=n_result_cols++] = &str_reason_col;
 	result_cols[record_route_col=n_result_cols++] = &str_record_route_col;
 	result_cols[record_route_col=n_result_cols++] = &str_record_route_col;
 	result_cols[version_col=n_result_cols++] = &str_version_col;
 	result_cols[version_col=n_result_cols++] = &str_version_col;
+	result_cols[updated_col=n_result_cols++] = &str_updated_col;
+	result_cols[updated_winfo_col=n_result_cols++] = &str_updated_winfo_col;
 	
 	
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) 
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) 
 	{
 	{
@@ -1451,6 +1534,9 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 		subs->record_route.len= record_route.len;
 		subs->record_route.len= record_route.len;
 	}
 	}
 
 
+	subs->updated= row_vals[updated_col].val.int_val;
+	subs->updated_winfo= row_vals[updated_winfo_col].val.int_val;
+
 	pa_dbf.free_result(pa_db, result);
 	pa_dbf.free_result(pa_db, result);
 	result= NULL;
 	result= NULL;
 
 
@@ -1484,6 +1570,74 @@ int handle_expired_subs(subs_t* s)
 
 
 }
 }
 
 
+void update_db_subs_timer_notifier(void)
+{
+	db_key_t query_cols[1], result_cols[3];
+	db_val_t query_vals[1], *values;
+	db_op_t query_ops[1];
+	db_row_t *rows;
+	db1_res_t *result = NULL;
+	int n_query_cols = 0, n_result_cols = 0;
+	int r_callid_col = 0, r_to_tag_col = 0, r_from_tag_col = 0;
+	int i;
+	subs_t subs;
+
+	if(pa_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		goto error;
+	}
+
+	if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		goto error;
+	}
+
+	query_cols[n_query_cols]= &str_expires_col;
+	query_vals[n_query_cols].type = DB1_INT;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.int_val= (int)time(NULL) - expires_offset;
+	query_ops[n_query_cols]= OP_LT;
+	n_query_cols++;
+
+	result_cols[r_callid_col=n_result_cols++] = &str_callid_col;
+	result_cols[r_to_tag_col=n_result_cols++] = &str_to_tag_col;
+	result_cols[r_from_tag_col=n_result_cols++] = &str_from_tag_col;
+
+	if(db_fetch_query(&pa_dbf, pres_fetch_rows, pa_db, query_cols,
+			  query_ops, query_vals, result_cols,
+			  n_query_cols, n_result_cols, 0, &result )< 0)
+	{
+		LM_ERR("Can't query db\n");
+		goto error;
+	}
+
+	if(result == NULL) goto error;
+
+	do {
+		rows = RES_ROWS(result);
+	
+		for (i = 0; i <RES_ROW_N(result); i++)
+		{
+			values = ROW_VALUES(rows);
+
+			subs.callid.s = (char *) VAL_STRING(&values[r_callid_col]);
+			subs.callid.len = strlen(subs.callid.s);
+			subs.to_tag.s = (char *) VAL_STRING(&values[r_to_tag_col]);
+			subs.to_tag.len = strlen(subs.to_tag.s);
+			subs.from_tag.s = (char *) VAL_STRING(&values[r_from_tag_col]);
+			subs.from_tag.len = strlen(subs.from_tag.s);
+
+			set_updated(&subs);
+		}
+	} while (db_fetch_next(&pa_dbf, pres_fetch_rows, pa_db, &result) == 1
+			&& RES_ROW_N(result) > 0);
+
+error:
+	if (result) pa_dbf.free_result(pa_db, result);
+}
+
 void update_db_subs_timer_dbonly(void)
 void update_db_subs_timer_dbonly(void)
 {
 {
 	db_op_t qops[1];
 	db_op_t qops[1];
@@ -1992,17 +2146,22 @@ void timer_db_update(unsigned int ticks,void *param)
 
 
 
 
 	switch (subs_dbmode) {
 	switch (subs_dbmode) {
-		case DB_ONLY:	update_db_subs_timer_dbonly();
-						break;
-		case NO_DB:		update_db_subs_timer_dbnone(no_lock);
-						break;
-		default:
-				if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0)
-				{
-					LM_ERR("sql use table failed\n");
-					return;
-				}
-				update_db_subs_timer(pa_db, pa_dbf, subs_htable, shtable_size,
+	case DB_ONLY:
+		if (pres_notifier_processes > 0)
+			update_db_subs_timer_notifier();
+		else
+			update_db_subs_timer_dbonly();
+	break;
+	case NO_DB:
+		update_db_subs_timer_dbnone(no_lock);
+	break;
+	default:
+		if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0)
+		{
+			LM_ERR("sql use table failed\n");
+			return;
+		}
+		update_db_subs_timer(pa_db, pa_dbf, subs_htable, shtable_size,
 						no_lock, handle_expired_subs);
 						no_lock, handle_expired_subs);
 	}
 	}
 }
 }
@@ -2072,6 +2231,11 @@ int restore_db_subs(void)
 		goto error;
 		goto error;
 	}
 	}
 
 
+	if (result == NULL)
+	{
+		LM_ERR("bad result\n");
+		goto error;
+	}
 
 
 	do {
 	do {
 		nr_rows = RES_ROW_N(result);
 		nr_rows = RES_ROW_N(result);
@@ -2220,72 +2384,6 @@ error:
 
 
 }
 }
 
 
-int refresh_watcher(str* pres_uri, str* watcher_uri, str* event, 
-		int status, str* reason)
-{
-	unsigned int hash_code;
-	subs_t* s, *s_copy;
-	pres_ev_t* ev;		
-	struct sip_uri uri;
-	str user, domain;
-	/* refresh status in subs_htable and send notify */
-
-	ev=	contains_event(event, NULL);
-	if(ev== NULL)
-	{
-		LM_ERR("while searching event in list\n");
-		return -1;
-	}
-
-	if(parse_uri(watcher_uri->s, watcher_uri->len, &uri)< 0)
-	{
-		LM_ERR("parsing uri\n");
-		return -1;
-	}
-	user= uri.user;
-	domain= uri.host;
-
-	hash_code= core_hash(pres_uri, event, shtable_size);
-
-	lock_get(&subs_htable[hash_code].lock);
-
-	s= subs_htable[hash_code].entries->next;
-
-	while(s)
-	{
-		if(s->event== ev && s->pres_uri.len== pres_uri->len &&
-			strncmp(s->pres_uri.s, pres_uri->s, pres_uri->len)== 0 &&
-			s->watcher_user.len==user.len && strncmp(s->watcher_user.s,user.s, user.len)==0 &&
-			s->watcher_domain.len== domain.len && 
-			strncmp(s->watcher_domain.s, domain.s, domain.len)== 0)
-		{
-			s->status= status;
-			if(reason)
-				s->reason= *reason;
-			
-			s_copy= mem_copy_subs(s, PKG_MEM_TYPE);
-			if(s_copy== NULL)
-			{
-				LM_ERR("copying subs_t\n");
-				lock_release(&subs_htable[hash_code].lock);
-				return -1;
-			}
-			lock_release(&subs_htable[hash_code].lock);
-			s_copy->local_cseq++;
-			if(notify(s_copy, NULL, NULL, 0)< 0)
-			{
-				LM_ERR("in notify function\n");
-				pkg_free(s_copy);
-				return -1;
-			}
-			pkg_free(s_copy);
-			lock_get(&subs_htable[hash_code].lock);
-		}
-		s= s->next;
-	}
-	return 0;
-}
-
 int get_db_subs_auth(subs_t* subs, int* found)
 int get_db_subs_auth(subs_t* subs, int* found)
 {
 {
 	db_key_t db_keys[5];
 	db_key_t db_keys[5];

+ 1 - 5
modules_k/presence/subscribe.h

@@ -85,6 +85,7 @@ struct subscription
 	int recv_event;
 	int recv_event;
 	int internal_update_flag;
 	int internal_update_flag;
 	int updated;
 	int updated;
+	int updated_winfo;
 	struct subscription* next;
 	struct subscription* next;
 
 
 };
 };
@@ -102,11 +103,6 @@ void timer_db_update(unsigned int ticks,void *param);
 
 
 int update_subs_db(subs_t* subs, int type);
 int update_subs_db(subs_t* subs, int type);
 
 
-int refresh_watcher(str* pres_uri, str* watcher_uri, str* event, 
-	int status, str* reason);
-
-typedef int (*refresh_watcher_t)(str*, str* , str* ,int , str* );
-
 int restore_db_subs(void);
 int restore_db_subs(void);
 
 
 typedef int (*handle_expired_func_t)(subs_t* );
 typedef int (*handle_expired_func_t)(subs_t* );

+ 44 - 0
modules_k/pua/pua_db.c

@@ -179,6 +179,12 @@ int clean_puadb( int update_period, int min_expires )
 		return(-1);
 		return(-1);
 	}
 	}
 
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(-1);
+	}
+
 	if (RES_ROW_N(res) == 0)
 	if (RES_ROW_N(res) == 0)
 	{
 	{
 		/* no match */ 
 		/* no match */ 
@@ -285,6 +291,12 @@ int is_dialog_puadb(ua_pres_t *pres)
 		return(-1);
 		return(-1);
 	}
 	}
 
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(-1);
+	}
+
 	nr_rows = RES_ROW_N(res);
 	nr_rows = RES_ROW_N(res);
 	pua_dbf.free_result(pua_db, res);
 	pua_dbf.free_result(pua_db, res);
 
 
@@ -366,6 +378,12 @@ int get_record_id_puadb(ua_pres_t *pres, str **rec_id )
 		return(-1);
 		return(-1);
 	}
 	}
 
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(-1);
+	}
+
 	nr_rows = RES_ROW_N(res);
 	nr_rows = RES_ROW_N(res);
 
 
 	switch (nr_rows)
 	switch (nr_rows)
@@ -392,6 +410,12 @@ int get_record_id_puadb(ua_pres_t *pres, str **rec_id )
 			return(-1);
 			return(-1);
 		}
 		}
 
 
+		if (res == NULL)
+		{
+			LM_ERR("bad result\n");
+			return(-1);
+		}
+
 		nr_rows = RES_ROW_N(res);
 		nr_rows = RES_ROW_N(res);
 
 
 		if (nr_rows == 1)
 		if (nr_rows == 1)
@@ -827,6 +851,12 @@ ua_pres_t *get_record_puadb(str pres_id, str *etag, ua_pres_t *result, db1_res_t
 		return(NULL);
 		return(NULL);
 	}
 	}
 
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(NULL);
+	}
+
 	nr_rows = RES_ROW_N(res);
 	nr_rows = RES_ROW_N(res);
 
 
 	if (nr_rows == 0)
 	if (nr_rows == 0)
@@ -1194,6 +1224,12 @@ ua_pres_t *get_dialog_puadb(str pres_id, str *pres_uri, ua_pres_t *result, db1_r
 		return(NULL);
 		return(NULL);
 	}
 	}
 
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(NULL);
+	}
+
 	nr_rows = RES_ROW_N(res);
 	nr_rows = RES_ROW_N(res);
 
 
 	if (nr_rows == 0)
 	if (nr_rows == 0)
@@ -1524,6 +1560,12 @@ list_entry_t *get_subs_list_puadb(str *did)
 		return list;
 		return list;
 	}
 	}
 
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return list;
+	}
+
 	if (RES_ROW_N(res) == 0)
 	if (RES_ROW_N(res) == 0)
 	{
 	{
 		LM_INFO( "No records found\n");
 		LM_INFO( "No records found\n");
@@ -1565,5 +1607,7 @@ list_entry_t *get_subs_list_puadb(str *did)
 	} while ((db_fetch_next(&pua_dbf, pua_fetch_rows, pua_db, &res)==1)
 	} while ((db_fetch_next(&pua_dbf, pua_fetch_rows, pua_db, &res)==1)
 			&& (RES_ROWS(res)>0));
 			&& (RES_ROWS(res)>0));
 
 
+	pua_dbf.free_result(pua_db, res);
+	
 	return list;
 	return list;
 }
 }

+ 4 - 1
modules_k/pua_reginfo/usrloc_cb.c

@@ -235,7 +235,10 @@ void reginfo_usrloc_cb(ucontact_t* c, int type, void* param) {
 			LM_ERR("Error allocating memory for URI!\n");
 			LM_ERR("Error allocating memory for URI!\n");
 			goto error;
 			goto error;
 		}
 		}
-		uri.len = snprintf(uri.s, uri.len, "sip:%.*s@%.*s", record->aor.len, record->aor.s, default_domain.len, default_domain.s);
+		if (record->aor.len > 0)
+			uri.len = snprintf(uri.s, uri.len, "sip:%.*s@%.*s", record->aor.len, record->aor.s, default_domain.len, default_domain.s);
+		else
+			uri.len = snprintf(uri.s, uri.len, "sip:%.*s", default_domain.len, default_domain.s);
 	} else {
 	} else {
 		uri.len = record->aor.len + 6;
 		uri.len = record->aor.len + 6;
 		uri.s = (char*)pkg_malloc(sizeof(char) * uri.len);
 		uri.s = (char*)pkg_malloc(sizeof(char) * uri.len);

+ 12 - 4
modules_k/purple/Makefile

@@ -1,12 +1,20 @@
 include ../../Makefile.defs
 include ../../Makefile.defs
 auto_gen=
 auto_gen=
 NAME=purple.so
 NAME=purple.so
-LIBS= 
+BUILDER = $(shell which pkg-config)
 
 
-DEFS+=-I/usr/lib/glib-2.0/include -I/usr/include/glib-2.0 \
+ifeq ($(BUILDER),)
+	DEFS+= -I/usr/lib/glib-2.0/include -I/usr/include/glib-2.0 \
 		-I/usr/include/libxml2
 		-I/usr/include/libxml2
-LIBS+=-lglib-2.0 -lpurple -lxml2
-
+	LIBS= -lglib-2.0 -lpurple -lxml2
+else
+	DEFS+= $(shell pkg-config --cflags glib-2.0)
+	LIBS=  $(shell pkg-config --libs glib-2.0)
+	DEFS+= $(shell pkg-config --cflags purple)
+	LIBS+= $(shell pkg-config --libs purple)
+	DEFS+= $(shell pkg-config --cflags libxml-2.0)
+	LIBS+= $(shell pkg-config --libs libxml-2.0)
+endif
 
 
 DEFS+=-DOPENSER_MOD_INTERFACE
 DEFS+=-DOPENSER_MOD_INTERFACE
 
 

+ 1 - 0
modules_k/purple/purple.h

@@ -20,6 +20,7 @@
 #ifndef _PURPLE_H
 #ifndef _PURPLE_H
 #define _PURPLE_H
 #define _PURPLE_H
 
 
+#include <glib.h>
 #include <libpurple/status.h>
 #include <libpurple/status.h>
 
 
 enum purple_cmd_type {
 enum purple_cmd_type {

+ 1 - 0
modules_k/purple/purplepipe.h

@@ -21,6 +21,7 @@
 #define _PURPLEPIPE_H
 #define _PURPLEPIPE_H
 
 
 #include <stdlib.h>
 #include <stdlib.h>
+#include <glib.h>
 #include <libpurple/savedstatuses.h>
 #include <libpurple/savedstatuses.h>
 #include <libpurple/status.h>
 #include <libpurple/status.h>
 
 

+ 1 - 1
modules_k/purple/utils.h

@@ -21,7 +21,7 @@
 #define _UTILS_H
 #define _UTILS_H
 
 
 #include <stdlib.h>
 #include <stdlib.h>
-
+#include <glib.h>
 #include <libpurple/savedstatuses.h>
 #include <libpurple/savedstatuses.h>
 #include <libpurple/status.h>
 #include <libpurple/status.h>
 
 

+ 0 - 9
modules_k/rls/README

@@ -267,15 +267,6 @@ modparam("rls", "xcap_db_url", "dbdriver://username:password@dbhost/dbname")
    in a database, allowing scalability at the expense of speed. Mode 1 is
    in a database, allowing scalability at the expense of speed. Mode 1 is
    reserved.
    reserved.
 
 
-   The RLS modules uses SQL BEGIN/COMMIT statements around related sets of
-   database queries to make sure they are a single transaction when
-   database only mode is used. This means you should ensure that automatic
-   retries/reconnects are disabled for your database (for example, by
-   setting the retries parameter for db_mysql or db_postgres to 0). This
-   is needed because if the connection fails any unCOMMITed queries will
-   be automatically rolled back by the database. This means that automatic
-   retries could result in data inconsistencies.
-
    Default value is “0”
    Default value is “0”
 
 
    Example 1.4. Set db_mode parameter
    Example 1.4. Set db_mode parameter

+ 2 - 13
modules_k/rls/doc/rls_admin.xml

@@ -171,19 +171,8 @@ modparam("rls", "xcap_db_url", "&exampledb;")
 		<para>
 		<para>
 		The module supports 2 modes of operation, high speed memory
 		The module supports 2 modes of operation, high speed memory
 		based storage (mode 0), and database only (mode 2) where all 
 		based storage (mode 0), and database only (mode 2) where all 
-		data is stored in a database, allowing scalability at the expense of speed.
-		Mode 1 is reserved.
-		</para>
-		<para>
-		The RLS modules uses SQL BEGIN/COMMIT statements around related
-		sets of database queries to make sure they are a single
-		transaction when database only mode is used.  This means you
-		should ensure that automatic retries/reconnects are disabled
-		for your database (for example, by setting the retries
-		parameter for db_mysql or db_postgres to 0).  This is needed
-		because if the connection fails any unCOMMITed queries will
-		be automatically rolled back by the database.  This means that
-		automatic retries could result in data inconsistencies.
+		data is stored in a database, allowing scalability at the
+		expense of speed. Mode 1 is reserved.
 		</para>
 		</para>
 		<para>
 		<para>
 		<emphasis>	Default value is <quote>0</quote> 	
 		<emphasis>	Default value is <quote>0</quote> 	

+ 36 - 32
modules_k/rls/notify.c

@@ -121,6 +121,11 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, str* rl_uri,
 	result_cols[pres_state_col= n_result_cols++]= &str_presence_state_col;
 	result_cols[pres_state_col= n_result_cols++]= &str_presence_state_col;
 	result_cols[auth_state_col= n_result_cols++]= &str_auth_state_col;
 	result_cols[auth_state_col= n_result_cols++]= &str_auth_state_col;
 	result_cols[reason_col= n_result_cols++]= &str_reason_col;
 	result_cols[reason_col= n_result_cols++]= &str_reason_col;
+
+	update_cols[0]= &str_updated_col;
+	update_vals[0].type = DB1_INT;
+	update_vals[0].nul = 0;
+	update_vals[0].val.int_val= NO_UPDATE_TYPE; 
 	
 	
 	if (rlpres_dbf.use_table(rlpres_db, &rlpres_table) < 0) 
 	if (rlpres_dbf.use_table(rlpres_db, &rlpres_table) < 0) 
 	{
 	{
@@ -143,8 +148,30 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, str* rl_uri,
 		LM_ERR("in sql query\n");
 		LM_ERR("in sql query\n");
 		goto error;
 		goto error;
 	}
 	}
-	if(result== NULL)
+	if(result == NULL)
+	{
+		LM_ERR("bad result\n");
 		goto error;
 		goto error;
+	}
+
+	if (result->n > 0)
+	{
+		if(rlpres_dbf.update(rlpres_db, query_cols, 0, query_vals,
+				     update_cols, update_vals, 1, 1) < 0)
+		{
+			LM_ERR("in sql update\n");
+			goto error;
+		}
+	}
+
+	if (dbmode == RLS_DB_ONLY && rlpres_dbf.end_transaction)
+	{
+		if (rlpres_dbf.end_transaction(rlpres_db) < 0)
+		{
+			LM_ERR("in end_transaction\n");
+			goto error;
+		}
+	}
 
 
 	/* Allocate an initial buffer for the multipart body.
 	/* Allocate an initial buffer for the multipart body.
 	 * This buffer will be reallocated if neccessary */
 	 * This buffer will be reallocated if neccessary */
@@ -238,9 +265,6 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, str* rl_uri,
 			&rlmi_cont->len, (rls_max_notify_body_len == 0));
 			&rlmi_cont->len, (rls_max_notify_body_len == 0));
 	xmlFreeDoc(rlmi_body);
 	xmlFreeDoc(rlmi_body);
 
 
-	rlpres_dbf.free_result(rlpres_db, result);
-	result= NULL;
-
 	if(agg_body_sendn_update(rl_uri, boundary_string, rlmi_cont,
 	if(agg_body_sendn_update(rl_uri, boundary_string, rlmi_cont,
 		multipart_body, subs, hash_code)< 0)
 		multipart_body, subs, hash_code)< 0)
 	{
 	{
@@ -248,33 +272,6 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, str* rl_uri,
 		goto error;
 		goto error;
 	}
 	}
 
 
-	/* update updated col in rlpres_table*/
-	update_cols[0]= &str_updated_col;
-	update_vals[0].type = DB1_INT;
-	update_vals[0].nul = 0;
-	update_vals[0].val.int_val= NO_UPDATE_TYPE; 
-	
-	if (rlpres_dbf.use_table(rlpres_db, &rlpres_table) < 0) 
-	{
-		LM_ERR("in use_table\n");
-		goto error;
-	}
-	if(rlpres_dbf.update(rlpres_db, query_cols, 0, query_vals, update_cols,
-					update_vals, 1, 1)< 0)
-	{
-		LM_ERR("in sql update\n");
-		goto error;
-	}
-
-	if (dbmode == RLS_DB_ONLY && rlpres_dbf.end_transaction)
-	{
-		if (rlpres_dbf.end_transaction(rlpres_db) < 0)
-		{
-			LM_ERR("in end_transaction\n");
-			goto error;
-		}
-	}
-
 	xmlFree(rlmi_cont->s);
 	xmlFree(rlmi_cont->s);
 	pkg_free(rlmi_cont);
 	pkg_free(rlmi_cont);
 
 
@@ -286,7 +283,8 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, str* rl_uri,
 	}
 	}
 	multipart_body_size = 0;
 	multipart_body_size = 0;
 	pkg_free(rlsubs_did.s);
 	pkg_free(rlsubs_did.s);
-
+	rlpres_dbf.free_result(rlpres_db, result);
+	
 	return 0;
 	return 0;
 error:
 error:
 	if(rlmi_cont)
 	if(rlmi_cont)
@@ -1325,6 +1323,12 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if(result == NULL)
+	{
+		LM_ERR("bad result\n");
+		return -1;
+	}
+
 	if(result->n<=0)
 	if(result->n<=0)
 	{
 	{
 		LM_DBG("No rl document found\n");
 		LM_DBG("No rl document found\n");

+ 51 - 47
modules_k/rls/resource_notify.c

@@ -847,6 +847,11 @@ static void timer_send_full_state_notifies(int round)
 	db_row_t *rows;
 	db_row_t *rows;
 	db1_res_t *result = NULL;
 	db1_res_t *result = NULL;
 	int n_result_cols = 0, i;
 	int n_result_cols = 0, i;
+	int pres_uri_col, tuser_col, tdomain_col, fuser_col, fdomain_col;
+	int wuser_col, wdomain_col, callid_col, to_tag_col, from_tag_col;
+	int sockinfo_col, lcontact_col, contact_col, rroute_col, event_id_col;
+	int reason_col, event_col, lcseq_col, rcseq_col, status_col;
+	int version_col, expires_col;
 	subs_t sub;
 	subs_t sub;
 	str ev_sname;
 	str ev_sname;
 	event_t parsed_event;
 	event_t parsed_event;
@@ -859,28 +864,28 @@ static void timer_send_full_state_notifies(int round)
 	query_vals[0].nul = 0;
 	query_vals[0].nul = 0;
 	query_vals[0].val.int_val = round;
 	query_vals[0].val.int_val = round;
 
 
-	result_cols[n_result_cols++] = &str_presentity_uri_col;
-	result_cols[n_result_cols++] = &str_to_user_col;
-	result_cols[n_result_cols++] = &str_to_domain_col;
-	result_cols[n_result_cols++] = &str_from_user_col;
-	result_cols[n_result_cols++] = &str_from_domain_col;
-	result_cols[n_result_cols++] = &str_watcher_username_col;
-	result_cols[n_result_cols++] = &str_watcher_domain_col;
-	result_cols[n_result_cols++] = &str_callid_col;
-	result_cols[n_result_cols++] = &str_to_tag_col;
-	result_cols[n_result_cols++] = &str_from_tag_col;
-	result_cols[n_result_cols++] = &str_socket_info_col;
-	result_cols[n_result_cols++] = &str_local_contact_col;
-	result_cols[n_result_cols++] = &str_contact_col;
-	result_cols[n_result_cols++] = &str_record_route_col;
-	result_cols[n_result_cols++] = &str_event_id_col;
-	result_cols[n_result_cols++] = &str_reason_col;
-	result_cols[n_result_cols++] = &str_event_col;
-	result_cols[n_result_cols++] = &str_local_cseq_col;
-	result_cols[n_result_cols++] = &str_remote_cseq_col;
-	result_cols[n_result_cols++] = &str_status_col;
-	result_cols[n_result_cols++] = &str_version_col;
-	result_cols[n_result_cols++] = &str_expires_col;
+	result_cols[pres_uri_col = n_result_cols++] = &str_presentity_uri_col;
+	result_cols[tuser_col = n_result_cols++] = &str_to_user_col;
+	result_cols[tdomain_col = n_result_cols++] = &str_to_domain_col;
+	result_cols[fuser_col = n_result_cols++] = &str_from_user_col;
+	result_cols[fdomain_col = n_result_cols++] = &str_from_domain_col;
+	result_cols[wuser_col = n_result_cols++] = &str_watcher_username_col;
+	result_cols[wdomain_col = n_result_cols++] = &str_watcher_domain_col;
+	result_cols[callid_col = n_result_cols++] = &str_callid_col;
+	result_cols[to_tag_col = n_result_cols++] = &str_to_tag_col;
+	result_cols[from_tag_col = n_result_cols++] = &str_from_tag_col;
+	result_cols[sockinfo_col = n_result_cols++] = &str_socket_info_col;
+	result_cols[lcontact_col = n_result_cols++] = &str_local_contact_col;
+	result_cols[contact_col = n_result_cols++] = &str_contact_col;
+	result_cols[rroute_col = n_result_cols++] = &str_record_route_col;
+	result_cols[event_id_col = n_result_cols++] = &str_event_id_col;
+	result_cols[reason_col = n_result_cols++] = &str_reason_col;
+	result_cols[event_col = n_result_cols++] = &str_event_col;
+	result_cols[lcseq_col = n_result_cols++] = &str_local_cseq_col;
+	result_cols[rcseq_col = n_result_cols++] = &str_remote_cseq_col;
+	result_cols[status_col = n_result_cols++] = &str_status_col;
+	result_cols[version_col = n_result_cols++] = &str_version_col;
+	result_cols[expires_col = n_result_cols++] = &str_expires_col;
 
 
 	update_cols[0] = &str_updated_col;
 	update_cols[0] = &str_updated_col;
 	update_vals[0].type = DB1_INT;
 	update_vals[0].type = DB1_INT;
@@ -936,23 +941,23 @@ static void timer_send_full_state_notifies(int round)
 	{
 	{
 		memset(&sub, 0, sizeof(subs_t));
 		memset(&sub, 0, sizeof(subs_t));
 		values = ROW_VALUES(&rows[i]);
 		values = ROW_VALUES(&rows[i]);
-		EXTRACT_STRING(sub.pres_uri, VAL_STRING(&values[0]));
-		EXTRACT_STRING(sub.to_user, VAL_STRING(&values[1]));
-		EXTRACT_STRING(sub.to_domain, VAL_STRING(&values[2]));
-		EXTRACT_STRING(sub.from_user, VAL_STRING(&values[3]));
-		EXTRACT_STRING(sub.from_domain, VAL_STRING(&values[4]));
-		EXTRACT_STRING(sub.watcher_user, VAL_STRING(&values[5]));
-		EXTRACT_STRING(sub.watcher_domain, VAL_STRING(&values[6]));
-		EXTRACT_STRING(sub.callid, VAL_STRING(&values[7]));
-		EXTRACT_STRING(sub.to_tag, VAL_STRING(&values[8]));
-		EXTRACT_STRING(sub.from_tag, VAL_STRING(&values[9]));
-		EXTRACT_STRING(sub.sockinfo_str, VAL_STRING(&values[10]));
-		EXTRACT_STRING(sub.local_contact, VAL_STRING(&values[11]));
-		EXTRACT_STRING(sub.contact, VAL_STRING(&values[12]));
-		EXTRACT_STRING(sub.record_route, VAL_STRING(&values[13]));
-		EXTRACT_STRING(sub.event_id, VAL_STRING(&values[14]));
-		EXTRACT_STRING(sub.reason, VAL_STRING(&values[15]));
-		EXTRACT_STRING(ev_sname, VAL_STRING(&values[16]));
+		EXTRACT_STRING(sub.pres_uri, VAL_STRING(&values[pres_uri_col]));
+		EXTRACT_STRING(sub.to_user, VAL_STRING(&values[tuser_col]));
+		EXTRACT_STRING(sub.to_domain, VAL_STRING(&values[tdomain_col]));
+		EXTRACT_STRING(sub.from_user, VAL_STRING(&values[fuser_col]));
+		EXTRACT_STRING(sub.from_domain, VAL_STRING(&values[fdomain_col]));
+		EXTRACT_STRING(sub.watcher_user, VAL_STRING(&values[wuser_col]));
+		EXTRACT_STRING(sub.watcher_domain, VAL_STRING(&values[wdomain_col]));
+		EXTRACT_STRING(sub.callid, VAL_STRING(&values[callid_col]));
+		EXTRACT_STRING(sub.to_tag, VAL_STRING(&values[to_tag_col]));
+		EXTRACT_STRING(sub.from_tag, VAL_STRING(&values[from_tag_col]));
+		EXTRACT_STRING(sub.sockinfo_str, VAL_STRING(&values[sockinfo_col]));
+		EXTRACT_STRING(sub.local_contact, VAL_STRING(&values[lcontact_col]));
+		EXTRACT_STRING(sub.contact, VAL_STRING(&values[contact_col]));
+		EXTRACT_STRING(sub.record_route, VAL_STRING(&values[rroute_col]));
+		EXTRACT_STRING(sub.event_id, VAL_STRING(&values[event_id_col]));
+		EXTRACT_STRING(sub.reason, VAL_STRING(&values[reason_col]));
+		EXTRACT_STRING(ev_sname, VAL_STRING(&values[event_col]));
 		sub.event = pres_contains_event(&ev_sname, &parsed_event);
 		sub.event = pres_contains_event(&ev_sname, &parsed_event);
 		if (sub.event == NULL)
 		if (sub.event == NULL)
 		{
 		{
@@ -960,12 +965,12 @@ static void timer_send_full_state_notifies(int round)
 			goto done;
 			goto done;
 		}
 		}
 
 
-		sub.local_cseq = VAL_INT(&values[17]);
-		sub.remote_cseq = VAL_INT(&values[18]);
-		sub.status = VAL_INT(&values[19]);
-		sub.version = VAL_INT(&values[20]);
-		if (VAL_INT(&values[21]) > now)
-			sub.expires = VAL_INT(&values[21]) - now;
+		sub.local_cseq = VAL_INT(&values[lcseq_col]);
+		sub.remote_cseq = VAL_INT(&values[rcseq_col]);
+		sub.status = VAL_INT(&values[status_col]);
+		sub.version = VAL_INT(&values[version_col]);
+		if (VAL_INT(&values[expires_col]) > now)
+			sub.expires = VAL_INT(&values[expires_col]) - now;
 		else
 		else
 			sub.expires = 0;
 			sub.expires = 0;
 
 
@@ -1063,10 +1068,9 @@ static void timer_send_update_notifies(int round)
 		LM_ERR("in sql query\n");
 		LM_ERR("in sql query\n");
 		goto done;
 		goto done;
 	}
 	}
-	if(result== NULL || result->n<= 0)
+	if(result == NULL || result->n <= 0)
 		goto done;
 		goto done;
 
 
-	/* update the rlpres table */
 	if(rlpres_dbf.update(rlpres_db, query_cols, 0, query_vals, update_cols,
 	if(rlpres_dbf.update(rlpres_db, query_cols, 0, query_vals, update_cols,
 					update_vals, 1, 1)< 0)
 					update_vals, 1, 1)< 0)
 	{
 	{

+ 12 - 36
modules_k/rls/rls_db.c

@@ -148,15 +148,6 @@ int delete_expired_subs_rlsdb( void )
 	result_cols[r_to_tag_col=n_result_cols++] = &str_to_tag_col;
 	result_cols[r_to_tag_col=n_result_cols++] = &str_to_tag_col;
 	result_cols[r_from_tag_col=n_result_cols++] = &str_from_tag_col;
 	result_cols[r_from_tag_col=n_result_cols++] = &str_from_tag_col;
 
 
-	if (rls_dbf.start_transaction)
-	{
-		if (rls_dbf.start_transaction(rls_db) < 0)
-		{
-			LM_ERR("in start_transaction\n");
-			goto error;
-		}
-	}
-
 	if(rls_dbf.query(rls_db, query_cols, query_ops, query_vals, result_cols, 
 	if(rls_dbf.query(rls_db, query_cols, query_ops, query_vals, result_cols, 
 				n_query_cols, n_result_cols, 0, &result )< 0)
 				n_query_cols, n_result_cols, 0, &result )< 0)
 	{
 	{
@@ -221,28 +212,12 @@ int delete_expired_subs_rlsdb( void )
 		pkg_free(rlsubs_did.s);
 		pkg_free(rlsubs_did.s);
 	}
 	}
 
 
-	if (rls_dbf.end_transaction)
-	{
-		if (rls_dbf.end_transaction(rls_db) < 0)
-		{
-			LM_ERR("in end_transaction\n");
-			goto error;
-		}
-	}
-
-	if(result) rls_dbf.free_result(rls_db, result);
+	rls_dbf.free_result(rls_db, result);
 	return 1;
 	return 1;
 
 
 error:
 error:
 	if (result) rls_dbf.free_result(rls_db, result);
 	if (result) rls_dbf.free_result(rls_db, result);
 	if (rlsubs_did.s) pkg_free(rlsubs_did.s);
 	if (rlsubs_did.s) pkg_free(rlsubs_did.s);
-
-	if (rls_dbf.abort_transaction)
-	{
-		if (rls_dbf.abort_transaction(rls_db) < 0)
-			LM_ERR("in abort_transaction\n");
-	}
-
 	return -1;
 	return -1;
 }
 }
 
 
@@ -739,6 +714,7 @@ int get_dialog_subscribe_rlsdb(subs_t *subs)
 	db_key_t result_cols[5];
 	db_key_t result_cols[5];
 	db_row_t *rows;
 	db_row_t *rows;
 	int n_query_cols = 0, n_result_cols = 0;
 	int n_query_cols = 0, n_result_cols = 0;
+	int pres_uri_col, rcseq_col, lcseq_col, version_col, rroute_col;
 	int nr_rows;
 	int nr_rows;
 	int r_remote_cseq, r_local_cseq, r_version;
 	int r_remote_cseq, r_local_cseq, r_version;
 	char *r_pres_uri, *r_record_route;
 	char *r_pres_uri, *r_record_route;
@@ -779,11 +755,11 @@ int get_dialog_subscribe_rlsdb(subs_t *subs)
 	query_vals[n_query_cols].val.str_val= subs->from_tag;
 	query_vals[n_query_cols].val.str_val= subs->from_tag;
 	n_query_cols++;
 	n_query_cols++;
 	
 	
-	result_cols[n_result_cols++] = &str_presentity_uri_col;
-	result_cols[n_result_cols++] = &str_remote_cseq_col;
-	result_cols[n_result_cols++] = &str_local_cseq_col;
-	result_cols[n_result_cols++] = &str_version_col;
-	result_cols[n_result_cols++] = &str_record_route_col;
+	result_cols[pres_uri_col = n_result_cols++] = &str_presentity_uri_col;
+	result_cols[rcseq_col = n_result_cols++] = &str_remote_cseq_col;
+	result_cols[lcseq_col = n_result_cols++] = &str_local_cseq_col;
+	result_cols[version_col = n_result_cols++] = &str_version_col;
+	result_cols[rroute_col = n_result_cols++] = &str_record_route_col;
 
 
 	if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols, 
 	if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols, 
 			n_query_cols, n_result_cols, 0, &result )< 0)
 			n_query_cols, n_result_cols, 0, &result )< 0)
@@ -816,11 +792,11 @@ int get_dialog_subscribe_rlsdb(subs_t *subs)
 	rows = RES_ROWS(result);
 	rows = RES_ROWS(result);
 	values = ROW_VALUES(rows);
 	values = ROW_VALUES(rows);
 
 
-	r_pres_uri = (char *)VAL_STRING(values+0);
-	r_remote_cseq = VAL_INT(values+1);
-	r_local_cseq = VAL_INT(values+2);
-	r_version = VAL_INT(values+3);
-	r_record_route = (char *)VAL_STRING(values+4);
+	r_pres_uri = (char *)VAL_STRING(&values[pres_uri_col]);
+	r_remote_cseq = VAL_INT(&values[rcseq_col]);
+	r_local_cseq = VAL_INT(&values[lcseq_col]);
+	r_version = VAL_INT(&values[version_col]);
+	r_record_route = (char *)VAL_STRING(&values[rroute_col]);
 
 
 	if(strlen(r_pres_uri) > 0)
 	if(strlen(r_pres_uri) > 0)
 	{
 	{

+ 6 - 0
modules_k/rls/subscribe.c

@@ -177,6 +177,12 @@ int rls_get_service_list(str *service_uri, str *user, str *domain,
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if (result == NULL)
+	{
+		LM_ERR("bad result\n");
+		return -1;
+	}
+
 	if(result->n<=0)
 	if(result->n<=0)
 	{
 	{
 		LM_DBG("No rl document found\n");
 		LM_DBG("No rl document found\n");

+ 1 - 0
modules_k/usrloc/Makefile

@@ -16,4 +16,5 @@ SERLIBPATH=../../lib
 SER_LIBS+=$(SERLIBPATH)/kmi/kmi
 SER_LIBS+=$(SERLIBPATH)/kmi/kmi
 SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
 SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
 SER_LIBS+=$(SERLIBPATH)/kcore/kcore
 SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/srutils/srutils
 include ../../Makefile.modules
 include ../../Makefile.modules

+ 9 - 1
modules_k/usrloc/ul_mi.c

@@ -35,6 +35,7 @@
 #include <string.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdio.h>
 #include "../../lib/kmi/mi.h"
 #include "../../lib/kmi/mi.h"
+#include "../../lib/srutils/sruid.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../qvalue.h"
 #include "../../qvalue.h"
@@ -49,12 +50,14 @@
 /*! CSEQ nr used */
 /*! CSEQ nr used */
 #define MI_UL_CSEQ 1
 #define MI_UL_CSEQ 1
 /*! call-id used for ul_add and ul_rm_contact */
 /*! call-id used for ul_add and ul_rm_contact */
-static str mi_ul_cid = str_init("dfjrewr12386fd6-343@openser.mi");
+static str mi_ul_cid = str_init("dfjrewr12386fd6-343@kamailio.mi");
 /*! user agent used for ul_add */
 /*! user agent used for ul_add */
 static str mi_ul_ua  = str_init("SIP Router MI Server");
 static str mi_ul_ua  = str_init("SIP Router MI Server");
 /*! path used for ul_add and ul_rm_contact */
 /*! path used for ul_add and ul_rm_contact */
 static str mi_ul_path = str_init("dummypath");
 static str mi_ul_path = str_init("dummypath");
 
 
+extern sruid_t _ul_sruid;
+
 /************************ helper functions ****************************/
 /************************ helper functions ****************************/
 
 
 /*!
 /*!
@@ -540,6 +543,10 @@ struct mi_root* mi_usrloc_add(struct mi_root *cmd, void *param)
 	if (str2int( &node->value, (unsigned int*)&ci.methods) < 0)
 	if (str2int( &node->value, (unsigned int*)&ci.methods) < 0)
 		goto bad_syntax;
 		goto bad_syntax;
 
 
+	if(sruid_next(&_ul_sruid)<0)
+		goto error;
+	ci.ruid = _ul_sruid.uid;
+
 	lock_udomain( dom, aor);
 	lock_udomain( dom, aor);
 
 
 	n = get_urecord( dom, aor, &r);
 	n = get_urecord( dom, aor, &r);
@@ -580,6 +587,7 @@ release_error:
 	release_urecord(r);
 	release_urecord(r);
 lock_error:
 lock_error:
 	unlock_udomain( dom, aor);
 	unlock_udomain( dom, aor);
+error:
 	return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 }
 }
 
 

+ 7 - 0
modules_k/usrloc/ul_mod.c

@@ -60,6 +60,7 @@
 #include "../../timer_proc.h" /* register_sync_timer */
 #include "../../timer_proc.h" /* register_sync_timer */
 #include "../../globals.h"   /* is_main */
 #include "../../globals.h"   /* is_main */
 #include "../../ut.h"        /* str_init */
 #include "../../ut.h"        /* str_init */
+#include "../../lib/srutils/sruid.h"
 #include "dlist.h"           /* register_udomain */
 #include "dlist.h"           /* register_udomain */
 #include "udomain.h"         /* {insert,delete,get,release}_urecord */
 #include "udomain.h"         /* {insert,delete,get,release}_urecord */
 #include "urecord.h"         /* {insert,delete,get}_ucontact */
 #include "urecord.h"         /* {insert,delete,get}_ucontact */
@@ -107,6 +108,9 @@ extern int ul_locks_no;
 int ul_db_update_as_insert = 0;
 int ul_db_update_as_insert = 0;
 int ul_timer_procs = 0;
 int ul_timer_procs = 0;
 
 
+/* sruid to get internal uid for mi/rpc commands */
+sruid_t _ul_sruid;
+
 /*
 /*
  * Module parameters and their default values
  * Module parameters and their default values
  */
  */
@@ -241,6 +245,9 @@ static int mod_init(void)
 	int i;
 	int i;
 	udomain_t* d;
 	udomain_t* d;
 
 
+	if(sruid_init(&_ul_sruid, '-', "ulcx", SRUID_INC)<0)
+		return -1;
+
 #ifdef STATISTICS
 #ifdef STATISTICS
 	/* register statistics */
 	/* register statistics */
 	if (register_module_stats( exports.name, mod_stats)!=0 ) {
 	if (register_module_stats( exports.name, mod_stats)!=0 ) {

+ 0 - 75
pkg/kamailio/fedora/kamailio.init

@@ -1,75 +0,0 @@
-#!/bin/bash
-#
-# Startup script for Kamailio
-#
-# chkconfig: - 85 15
-# description: Kamailio is a fast SIP Proxy.
-#
-# processname: kamailio
-# pidfile: /var/run/kamailio.pid
-# config: /etc/kamailio/kamailio.cfg
-#
-### BEGIN INIT INFO
-# Provides: kamailio
-# Required-Start: $local_fs $network $named
-# Should-Start: mysqld postgresql
-# Short-Description: start, stop Open SIP express router
-# Description: Kamailio or OPEN SIP Express Router is a very fast and flexible
-# 	SIP (RFC3261) proxy server.
-### END INIT INFO
-
-# Source function library.
-. /etc/rc.d/init.d/functions
-
-oser=/usr/sbin/kamailio
-prog=kamailio
-RETVAL=0
-
-[ -f /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
-
-start() {
-	echo -n $"Starting $prog: "
-	# there is something at end of this output which is needed to
-	# report proper [ OK ] status in Fedora scripts
-	daemon $oser $OPTIONS 2>/dev/null | tail -1
-	RETVAL=$?
-	echo
-	[ $RETVAL = 0 ] && touch /var/lock/subsys/$prog
-}
-
-stop() {
-	echo -n $"Stopping $prog: "
-	killproc $oser
-	RETVAL=$?
-	echo
-	[ $RETVAL = 0 ] && rm -f /var/lock/subsys/$prog /var/run/$prog.pid
-}
-
-# See how we were called.
-case "$1" in
-	start)
-		start
-		;;
-	stop)
-		stop
-		;;
-	status)
-		status $oser
-		RETVAL=$?
-		;;
-	restart|reload)
-		stop
-		start
-		;;
-	condrestart)
-		if [ -f /var/run/kamailio.pid ] ; then
-			stop
-			start
-		fi
-		;;
-	*)
-		echo $"Usage: $prog {start|stop|reload|restart|condrestart|status|help}"
-		exit 1
-esac
-
-exit $RETVAL

+ 12 - 0
pkg/kamailio/fedora/kamailio.service

@@ -0,0 +1,12 @@
+[Unit]
+Description=Kamailio (OpenSER) - the Open Source SIP Server
+After=syslog.target network.target
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/kamailio
+PIDFile=/var/run/kamailio.pid
+ExecStart=/usr/sbin/kamailio $OPTIONS
+
+[Install]
+WantedBy=multi-user.target

+ 881 - 0
pkg/kamailio/fedora/kamailio.spec

@@ -0,0 +1,881 @@
+%define name    kamailio
+%define ver     3.3.0
+%define rel     pre1
+%define _sharedir %{_prefix}/share
+
+
+
+Summary:       Kamailio (OpenSER) - the Open Source SIP Server
+Name:          %name
+Version:       %ver
+Release:       %rel
+Packager:      Peter Dunkley <[email protected]>
+License:       GPL
+Group:         System Environment/Daemons
+Source:        http://kamailio.org/pub/kamailio/%{ver}/src/%{name}-%{ver}_src.tar.gz
+URL:           http://kamailio.org/
+Vendor:        kamailio.org
+BuildRoot:     %{_tmppath}/%{name}-%{ver}-buildroot
+Conflicts:     kamailio-mysql < %ver, kamailio-postgresql < %ver
+Conflicts:     kamailio-unixODBC < %ver, kamailio-bdb < %ver
+Conflicts:     kamailio-sqlite < %ver, kamailio-utils < %ver
+Conflicts:     kamailio-cpl < %ver, kamailio-radius < %ver
+Conflicts:     kamailio-snmpstats < %ver, kamailio-presence < %ver
+Conflicts:     kamailio-xmpp < %ver, kamailio-tls < %ver
+Conflicts:     kamailio-carrierroute < %ver, kamailio-purple < %ver
+Conflicts:     kamailio-ldap < %ver, kamailio-xmlrpc < %ver
+Conflicts:     kamailio-perl < %ver, kamailio-lua < %ver
+Conflicts:     kamailio-python < %ver, kamailio-GeoIP < %ver
+Conflicts:     kamailio-regex < %ver, kamailio-dialplan < %ver
+Conflicts:     kamailio-lcr < %ver, kamailio-xmlops < %ver
+Conflicts:     kamailio-redis < %ver, kamailio-json < %ver
+Conflicts:     kamailio-mono < %ver
+BuildRequires: make flex bison
+
+%description
+Kamailio (former OpenSER) is an Open Source SIP Server released under GPL, able
+to handle thousands of call setups per second. Among features: asynchronous TCP,
+UDP and SCTP, secure communication via TLS for VoIP (voice, video); IPv4 and
+IPv6; SIMPLE instant messaging and presence with embedded XCAP server and MSRP
+relay; ENUM; DID and least cost routing; load balancing; routing fail-over;
+accounting, authentication and authorization; support for many backend systems
+such as MySQL, Postgres, Oracle, Radius, LDAP, Redis, Cassandra; XMLRPC control
+interface, SNMP monitoring. It can be used to build large VoIP servicing
+platforms or to scale up SIP-to-PSTN gateways, PBX systems or media servers
+like Asterisk™, FreeSWITCH™ or SEMS.
+
+
+%package mysql
+Summary:       MySQL database connectivity for Kamailio.
+Group:         System Environment/Daemons
+Requires:      mysql-libs, kamailio = %ver
+BuildRequires: mysql-devel zlib-devel
+
+%description mysql
+MySQL database connectivity for Kamailio.
+
+
+%package postgresql
+Summary:       PostgreSQL database connectivity for Kamailio.
+Group:         System Environment/Daemons
+Requires:      postgresql-libs, kamailio = %ver
+BuildRequires: postgresql-devel
+
+%description postgresql
+PostgreSQL database connectivity for Kamailio.
+
+
+%package unixODBC
+Summary:       unixODBC database connectivity for Kamailio.
+Group:         System Environment/Daemons
+Requires:      unixODBC, kamailio = %ver
+BuildRequires: unixODBC-devel
+
+%description unixODBC
+unixODBC database connectivity for Kamailio.
+
+
+%package bdb
+Summary:       Berkeley database connectivity for Kamailio.
+Group:         System Environment/Daemons
+Requires:      db4, kamailio = %ver
+BuildRequires: db4-devel
+
+%description bdb
+Berkeley database connectivity for Kamailio.
+
+
+%package sqlite
+Summary:       SQLite database connectivity for Kamailio.
+Group:         System Environment/Daemons
+Requires:      sqlite, kamailio = %ver
+BuildRequires: sqlite-devel
+
+%description sqlite
+SQLite database connectivity for Kamailio.
+
+
+%package utils
+Summary:       Non-SIP utitility functions for Kamailio.
+Group:         System Environment/Daemons
+Requires:      libcurl, libxml2, kamailio = %ver
+BuildRequires: libcurl-devel, libxml2-devel
+
+%description utils
+Non-SIP utitility functions for Kamailio.
+
+
+%package cpl
+Summary:       CPL (Call Processing Language) interpreter for Kamailio.
+Group:         System Environment/Daemons
+Requires:      libxml2, kamailio = %ver
+BuildRequires: libxml2-devel
+
+%description cpl
+CPL (Call Processing Language) interpreter for Kamailio.
+
+
+%package radius
+Summary:       Radius AAA API for Kamailio.
+Group:         System Environment/Daemons
+Requires:      radiusclient-ng, kamailio = %ver
+BuildRequires: radiusclient-ng-devel
+
+%description radius
+Radius AAA API for Kamailio.
+
+
+%package snmpstats
+Summary:       SNMP management interface (scalar statistics) for Kamailio.
+Group:         System Environment/Daemons
+Requires:      net-snmp-agent-libs, kamailio = %ver
+BuildRequires: lm_sensors-devel, net-snmp-devel
+
+%description snmpstats
+SNMP management interface (scalar statistics) for Kamailio.
+
+
+%package presence
+Summary:       SIP Presence (and RLS, XCAP, etc) support for Kamailio.
+Group:         System Environment/Daemons
+Requires:      libxml2, libcurl, kamailio = %ver, kamailio-xmpp = %ver
+BuildRequires: libxml2-devel, libcurl-devel
+
+%description presence
+SIP Presence (and RLS, XCAP, etc) support for Kamailio.
+
+
+%package xmpp
+Summary:       SIP/XMPP IM gateway for Kamailio.
+Group:         System Environment/Daemons
+Requires:      expat, kamailio = %ver
+BuildRequires: expat-devel
+
+%description xmpp
+SIP/XMPP IM gateway for Kamailio.
+
+
+%package tls
+Summary:       TLS transport for Kamailio.
+Group:         System Environment/Daemons
+Requires:      openssl, kamailio = %ver
+BuildRequires: openssl-devel
+
+%description tls
+TLS transport for Kamailio.
+
+
+%package carrierroute
+Summary:       Routing, balancing, and blacklisting for Kamailio.
+Group:         System Environment/Daemons
+Requires:      libconfuse, kamailio = %ver
+BuildRequires: libconfuse-devel
+
+%description carrierroute
+Routing, balancing, and blacklisting for Kamailio.
+
+
+%package  purple
+Summary:  Multi-protocol IM and presence gateway module.
+Group:    System Environment/Daemons
+Requires: glib, libpurple, libxml2, kamailio = %ver, kamailio-presence = %ver
+BuildRequires: glib-devel, libpurple-devel, libxml2-devel
+
+%description purple
+Multi-protocol IM and presence gateway module.
+
+
+%package ldap
+Summary:       LDAP search interface for Kamailio.
+Group:         System Environment/Daemons
+Requires:      openldap, kamailio = %ver
+BuildRequires: openldap-devel
+
+%description ldap
+LDAP search interface for Kamailio.
+
+
+%package xmlrpc
+Summary:       XMLRPC trasnport and encoding for Kamailio RPCs.
+Group:         System Environment/Daemons
+Requires:      libxml2, kamailio = %ver
+BuildRequires: libxml2-devel
+
+%description xmlrpc
+XMLRPC trasnport and encoding for Kamailio RPCs.
+
+
+%package perl
+Summary:       Perl extensions and database driver for Kamailio.
+Group:         System Environment/Daemons 
+Requires:      mod_perl, kamailio = %ver
+BuildRequires: mod_perl-devel
+
+%description perl
+Perl extensions and database driver for Kamailio.
+
+
+%package lua
+Summary:       Lua extensions for Kamailio.
+Group:         System Environment/Daemons
+Requires:      kamailio = %ver
+BuildRequires: lua-devel
+
+%description lua
+Lua extensions for Kamailio.
+
+
+%package python
+Summary:       Python extensions for Kamailio.
+Group:         System Environment/Daemons
+Requires:      python, kamailio = %ver
+BuildRequires: python-devel
+
+%description python
+Python extensions for Kamailio.
+
+
+%package GeoIP
+Summary:       Max Mind GeoIP real-time query support for Kamailio.
+Group:         System Environment/Daemons
+Requires:      GeoIP, kamailio = %ver
+BuildRequires: GeoIP-devel
+
+%description GeoIP
+Max Mind GeoIP real-time query support for Kamailio.
+
+
+%package regex
+Summary:       PCRE mtaching operations for Kamailio.
+Group:         System Environment/Daemons
+Requires:      pcre, kamailio = %ver
+BuildRequires: pcre-devel
+
+%description regex
+PCRE mtaching operations for Kamailio.
+
+
+%package dialplan
+Summary:       String translations based on rules for Kamailio.
+Group:         System Environment/Daemons
+Requires:      pcre, kamailio = %ver
+BuildRequires: pcre-devel
+
+%description dialplan
+String translations based on rules for Kamailio.
+
+
+%package lcr
+Summary:       Least cost routing for Kamailio.
+Group:         System Environment/Daemons
+Requires:      pcre, kamailio = %ver
+BuildRequires: pcre-devel
+
+%description lcr
+Least cost routing for Kamailio.
+
+
+%package xmlops
+Summary:       XML operation functions for Kamailio.
+Group:         System Environment/Daemons
+Requires:      libxml2, kamailio = %ver
+BuildRequires: libxml2-devel
+
+%description xmlops
+XML operation functions for Kamailio.
+
+
+%package redis
+Summary:       REDIS NoSQL database connector for Kamailio.
+Group:         System Environment/Daemons
+Requires:      hiredis, kamailio = %ver
+BuildRequires: hiredis-devel
+
+%description redis
+REDIS NoSQL database connector for Kamailio.
+
+
+%package json
+Summary:       json string operation and rpc support for Kamailio.
+Group:         System Environment/Daemons
+Requires:      json-c, libevent, kamailio = %ver
+BuildRequires: json-c-devel, libevent-devel
+
+%description json
+json string operation and rpc support for Kamailio.
+
+
+%package mono
+Summary:       Mono extensions for Kamailio.
+Group:         System Environment/Daemons
+Requires:      mono-core, kamailio = %ver
+BuildRequires: mono-devel
+
+%description mono
+Mono extensions for Kamailio.
+
+
+
+%prep
+%setup -n %{name}-%{ver}
+
+
+
+%build
+make FLAVOUR=kamailio cfg prefix=/usr cfg_prefix=$RPM_BUILD_ROOT\
+	basedir=$RPM_BUILD_ROOT cfg_target=/%{_sysconfdir}/kamailio/\
+	modules_dirs="modules modules_k"
+make
+make every-module skip_modules="auth_identity db_cassandra iptrtpproxy\
+	db_oracle memcached mi_xmlrpc osp" group_include="kstandard kmysql\
+	kpostgres kunixodbc kldap kperl kpython klua kutils kpurple ktls kxmpp\
+	kcpl ksnmpstats kcarrierroute kpresence kradius kgeoip kregex kdialplan\
+	klcr ksqlite kredis kjson kmono kberkeley" include_modules="xmlrpc\
+	xmlops"
+
+
+
+%install
+[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT"
+
+make install
+make install-modules-all skip_modules="auth_identity db_cassandra iptrtpproxy\
+	db_oracle memcached mi_xmlrpc osp" group_include="kstandard kmysql\
+	kpostgres kunixodbc kldap kperl kpython klua kutils kpurple ktls kxmpp\
+	kcpl ksnmpstats kcarrierroute kpresence kradius kgeoip kregex kdialplan\
+	klcr ksqlite kredis kjson kmono kberkeley" include_modules="xmlrpc\
+	xmlops"
+
+mkdir -p $RPM_BUILD_ROOT/%{_unitdir}
+install -m644 pkg/kamailio/fedora/kamailio.service \
+		$RPM_BUILD_ROOT/%{_unitdir}/kamailio.service
+
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig
+install -m644 pkg/kamailio/fedora/kamailio.sysconfig \
+		$RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/kamailio
+
+
+
+%pre
+/usr/sbin/groupadd -r kamailio 2> /dev/null || :
+/usr/sbin/useradd -r -g kamailio -s /bin/false -c "Kamailio daemon" -d \
+		%{_libdir}/kamailio kamailio 2> /dev/null || :
+
+
+
+%clean
+rm -rf "$RPM_BUILD_ROOT"
+
+
+
+%post
+/bin/systemctl --system daemon-reload
+
+
+
+%preun
+if [ $1 = 0 ]; then
+	/bin/systemctl stop kamailio.service
+	/bin/systemctl disable kamailio.service 2> /dev/null
+fi
+
+
+
+%files
+%defattr(-,root,root)
+%dir %{_docdir}/kamailio
+%doc %{_docdir}/kamailio/AUTHORS
+%doc %{_docdir}/kamailio/NEWS
+%doc %{_docdir}/kamailio/INSTALL
+%doc %{_docdir}/kamailio/README
+%doc %{_docdir}/kamailio/README-MODULES
+%doc %{_docdir}/kamailio/modules/README.async
+%doc %{_docdir}/kamailio/modules/README.auth
+%doc %{_docdir}/kamailio/modules/README.avpops
+%doc %{_docdir}/kamailio/modules/README.blst
+%doc %{_docdir}/kamailio/modules/README.cfg_db
+%doc %{_docdir}/kamailio/modules/README.cfg_rpc
+%doc %{_docdir}/kamailio/modules/README.counters
+%doc %{_docdir}/kamailio/modules/README.ctl
+%doc %{_docdir}/kamailio/modules/README.db_flatstore
+%doc %{_docdir}/kamailio/modules/README.debugger
+%doc %{_docdir}/kamailio/modules/README.enum
+%doc %{_docdir}/kamailio/modules/README.ipops
+%doc %{_docdir}/kamailio/modules/README.malloc_test
+%doc %{_docdir}/kamailio/modules/README.matrix
+%doc %{_docdir}/kamailio/modules/README.mediaproxy
+%doc %{_docdir}/kamailio/modules/README.mi_rpc
+%doc %{_docdir}/kamailio/modules/README.mqueue
+%doc %{_docdir}/kamailio/modules/README.msrp
+%doc %{_docdir}/kamailio/modules/README.mtree
+%doc %{_docdir}/kamailio/modules/README.pdb
+%doc %{_docdir}/kamailio/modules/README.pipelimit
+%doc %{_docdir}/kamailio/modules/README.prefix_route
+%doc %{_docdir}/kamailio/modules/README.ratelimit
+%doc %{_docdir}/kamailio/modules/README.rtpproxy
+%doc %{_docdir}/kamailio/modules/README.sanity
+%doc %{_docdir}/kamailio/modules/README.sdpops
+%doc %{_docdir}/kamailio/modules/README.sipcapture
+%doc %{_docdir}/kamailio/modules/README.sl
+%doc %{_docdir}/kamailio/modules/README.sms
+%doc %{_docdir}/kamailio/modules/README.textopsx
+%doc %{_docdir}/kamailio/modules/README.tm
+%doc %{_docdir}/kamailio/modules/README.tmrec
+%doc %{_docdir}/kamailio/modules/README.topoh
+%doc %{_docdir}/kamailio/modules/README.xhttp
+%doc %{_docdir}/kamailio/modules/README.xhttp_rpc
+%doc %{_docdir}/kamailio/modules_k/README.acc
+%doc %{_docdir}/kamailio/modules_k/README.alias_db
+%doc %{_docdir}/kamailio/modules_k/README.auth_db
+%doc %{_docdir}/kamailio/modules_k/README.auth_diameter
+%doc %{_docdir}/kamailio/modules_k/README.benchmark
+%doc %{_docdir}/kamailio/modules_k/README.call_control
+%doc %{_docdir}/kamailio/modules_k/README.cfgutils
+%doc %{_docdir}/kamailio/modules_k/README.db_cluster
+%doc %{_docdir}/kamailio/modules_k/README.db_text
+%doc %{_docdir}/kamailio/modules_k/README.dialog
+%doc %{_docdir}/kamailio/modules_k/README.dispatcher
+%doc %{_docdir}/kamailio/modules_k/README.diversion
+%doc %{_docdir}/kamailio/modules_k/README.dmq
+%doc %{_docdir}/kamailio/modules_k/README.domain
+%doc %{_docdir}/kamailio/modules_k/README.domainpolicy
+%doc %{_docdir}/kamailio/modules_k/README.drouting
+%doc %{_docdir}/kamailio/modules_k/README.exec
+%doc %{_docdir}/kamailio/modules_k/README.group
+%doc %{_docdir}/kamailio/modules_k/README.htable
+%doc %{_docdir}/kamailio/modules_k/README.imc
+%doc %{_docdir}/kamailio/modules_k/README.kex
+%doc %{_docdir}/kamailio/modules_k/README.maxfwd
+%doc %{_docdir}/kamailio/modules_k/README.mi_datagram
+%doc %{_docdir}/kamailio/modules_k/README.mi_fifo
+%doc %{_docdir}/kamailio/modules_k/README.msilo
+%doc %{_docdir}/kamailio/modules_k/README.nat_traversal
+%doc %{_docdir}/kamailio/modules_k/README.nathelper
+%doc %{_docdir}/kamailio/modules_k/README.p_usrloc
+%doc %{_docdir}/kamailio/modules_k/README.path
+%doc %{_docdir}/kamailio/modules_k/README.pdt
+%doc %{_docdir}/kamailio/modules_k/README.permissions
+%doc %{_docdir}/kamailio/modules_k/README.pike
+%doc %{_docdir}/kamailio/modules_k/README.pv
+%doc %{_docdir}/kamailio/modules_k/README.qos
+%doc %{_docdir}/kamailio/modules_k/README.registrar
+%doc %{_docdir}/kamailio/modules_k/README.rr
+%doc %{_docdir}/kamailio/modules_k/README.rtimer
+%doc %{_docdir}/kamailio/modules_k/README.seas
+%doc %{_docdir}/kamailio/modules_k/README.siptrace
+%doc %{_docdir}/kamailio/modules_k/README.siputils
+%doc %{_docdir}/kamailio/modules_k/README.speeddial
+%doc %{_docdir}/kamailio/modules_k/README.sqlops
+%doc %{_docdir}/kamailio/modules_k/README.sst
+%doc %{_docdir}/kamailio/modules_k/README.statistics
+%doc %{_docdir}/kamailio/modules_k/README.textops
+%doc %{_docdir}/kamailio/modules_k/README.tmx
+%doc %{_docdir}/kamailio/modules_k/README.uac
+%doc %{_docdir}/kamailio/modules_k/README.uac_redirect
+%doc %{_docdir}/kamailio/modules_k/README.uri_db
+%doc %{_docdir}/kamailio/modules_k/README.userblacklist
+%doc %{_docdir}/kamailio/modules_k/README.usrloc
+%doc %{_docdir}/kamailio/modules_k/README.xlog
+
+%dir %{_sysconfdir}/kamailio
+%config(noreplace) %{_sysconfdir}/kamailio/*
+%config %{_unitdir}/*
+%config %{_sysconfdir}/sysconfig/*
+
+%dir %{_libdir}/kamailio
+%{_libdir}/kamailio/libbinrpc.so
+%{_libdir}/kamailio/libbinrpc.so.0
+%{_libdir}/kamailio/libbinrpc.so.0.1
+%{_libdir}/kamailio/libkcore.so
+%{_libdir}/kamailio/libkcore.so.1
+%{_libdir}/kamailio/libkcore.so.1.0
+%{_libdir}/kamailio/libkmi.so
+%{_libdir}/kamailio/libkmi.so.1
+%{_libdir}/kamailio/libkmi.so.1.0
+%{_libdir}/kamailio/libsrdb1.so
+%{_libdir}/kamailio/libsrdb1.so.1
+%{_libdir}/kamailio/libsrdb1.so.1.0
+%{_libdir}/kamailio/libsrdb2.so
+%{_libdir}/kamailio/libsrdb2.so.1
+%{_libdir}/kamailio/libsrdb2.so.1.0
+%{_libdir}/kamailio/libsrutils.so
+%{_libdir}/kamailio/libsrutils.so.1
+%{_libdir}/kamailio/libsrutils.so.1.0
+%{_libdir}/kamailio/libtrie.so
+%{_libdir}/kamailio/libtrie.so.1
+%{_libdir}/kamailio/libtrie.so.1.0
+
+%dir %{_libdir}/kamailio/modules
+%{_libdir}/kamailio/modules/auth.so
+%{_libdir}/kamailio/modules/async.so
+%{_libdir}/kamailio/modules/avpops.so
+%{_libdir}/kamailio/modules/blst.so
+%{_libdir}/kamailio/modules/cfg_db.so
+%{_libdir}/kamailio/modules/cfg_rpc.so
+%{_libdir}/kamailio/modules/counters.so
+%{_libdir}/kamailio/modules/ctl.so
+%{_libdir}/kamailio/modules/db_flatstore.so
+%{_libdir}/kamailio/modules/debugger.so
+%{_libdir}/kamailio/modules/enum.so
+%{_libdir}/kamailio/modules/ipops.so
+%{_libdir}/kamailio/modules/malloc_test.so
+%{_libdir}/kamailio/modules/matrix.so
+%{_libdir}/kamailio/modules/mediaproxy.so
+%{_libdir}/kamailio/modules/mi_rpc.so
+%{_libdir}/kamailio/modules/mqueue.so
+%{_libdir}/kamailio/modules/msrp.so
+%{_libdir}/kamailio/modules/mtree.so
+%{_libdir}/kamailio/modules/pdb.so
+%{_libdir}/kamailio/modules/pipelimit.so
+%{_libdir}/kamailio/modules/prefix_route.so
+%{_libdir}/kamailio/modules/ratelimit.so
+%{_libdir}/kamailio/modules/rtpproxy.so
+%{_libdir}/kamailio/modules/sanity.so
+%{_libdir}/kamailio/modules/sipcapture.so
+%{_libdir}/kamailio/modules/sl.so
+%{_libdir}/kamailio/modules/sdpops.so
+%{_libdir}/kamailio/modules/sms.so
+%{_libdir}/kamailio/modules/tm.so
+%{_libdir}/kamailio/modules/tmrec.so
+%{_libdir}/kamailio/modules/textopsx.so
+%{_libdir}/kamailio/modules/topoh.so
+%{_libdir}/kamailio/modules/xhttp.so
+%{_libdir}/kamailio/modules/xhttp_rpc.so
+
+%dir %{_libdir}/kamailio/modules_k
+%{_libdir}/kamailio/modules_k/acc.so
+%{_libdir}/kamailio/modules_k/alias_db.so
+%{_libdir}/kamailio/modules_k/auth_db.so
+%{_libdir}/kamailio/modules_k/auth_diameter.so
+%{_libdir}/kamailio/modules_k/benchmark.so
+%{_libdir}/kamailio/modules_k/call_control.so
+%{_libdir}/kamailio/modules_k/cfgutils.so
+%{_libdir}/kamailio/modules_k/db_cluster.so
+%{_libdir}/kamailio/modules_k/db_text.so
+%{_libdir}/kamailio/modules_k/dialog.so
+%{_libdir}/kamailio/modules_k/dispatcher.so
+%{_libdir}/kamailio/modules_k/diversion.so
+%{_libdir}/kamailio/modules_k/dmq.so
+%{_libdir}/kamailio/modules_k/domain.so
+%{_libdir}/kamailio/modules_k/domainpolicy.so
+%{_libdir}/kamailio/modules_k/drouting.so
+%{_libdir}/kamailio/modules_k/exec.so
+%{_libdir}/kamailio/modules_k/group.so
+%{_libdir}/kamailio/modules_k/htable.so
+%{_libdir}/kamailio/modules_k/imc.so
+%{_libdir}/kamailio/modules_k/kex.so
+%{_libdir}/kamailio/modules_k/maxfwd.so
+%{_libdir}/kamailio/modules_k/mi_datagram.so
+%{_libdir}/kamailio/modules_k/mi_fifo.so
+%{_libdir}/kamailio/modules_k/msilo.so
+%{_libdir}/kamailio/modules_k/nat_traversal.so
+%{_libdir}/kamailio/modules_k/nathelper.so
+%{_libdir}/kamailio/modules_k/p_usrloc.so
+%{_libdir}/kamailio/modules_k/path.so
+%{_libdir}/kamailio/modules_k/pdt.so
+%{_libdir}/kamailio/modules_k/permissions.so
+%{_libdir}/kamailio/modules_k/pike.so
+%{_libdir}/kamailio/modules_k/pv.so
+%{_libdir}/kamailio/modules_k/qos.so
+%{_libdir}/kamailio/modules_k/registrar.so
+%{_libdir}/kamailio/modules_k/rr.so
+%{_libdir}/kamailio/modules_k/rtimer.so
+%{_libdir}/kamailio/modules_k/seas.so
+%{_libdir}/kamailio/modules_k/siptrace.so
+%{_libdir}/kamailio/modules_k/siputils.so
+%{_libdir}/kamailio/modules_k/speeddial.so
+%{_libdir}/kamailio/modules_k/sqlops.so
+%{_libdir}/kamailio/modules_k/sst.so
+%{_libdir}/kamailio/modules_k/statistics.so
+%{_libdir}/kamailio/modules_k/textops.so
+%{_libdir}/kamailio/modules_k/tmx.so
+%{_libdir}/kamailio/modules_k/uac.so
+%{_libdir}/kamailio/modules_k/uac_redirect.so
+%{_libdir}/kamailio/modules_k/uri_db.so
+%{_libdir}/kamailio/modules_k/userblacklist.so
+%{_libdir}/kamailio/modules_k/usrloc.so
+%{_libdir}/kamailio/modules_k/xlog.so
+
+%{_sbindir}/kamailio
+%{_sbindir}/kamctl
+%{_sbindir}/kamdbctl
+%{_sbindir}/sercmd
+%{_libdir}/kamailio/kamctl/dbtextdb/dbtextdb.py
+%{_libdir}/kamailio/kamctl/kamctl.base
+%{_libdir}/kamailio/kamctl/kamctl.ctlbase
+%{_libdir}/kamailio/kamctl/kamctl.dbtext
+%{_libdir}/kamailio/kamctl/kamctl.fifo
+%{_libdir}/kamailio/kamctl/kamctl.ser
+%{_libdir}/kamailio/kamctl/kamctl.ser_mi
+%{_libdir}/kamailio/kamctl/kamctl.sqlbase
+%{_libdir}/kamailio/kamctl/kamctl.unixsock
+%{_libdir}/kamailio/kamctl/kamdbctl.base
+%{_libdir}/kamailio/kamctl/kamdbctl.dbtext
+
+%{_mandir}/man5/*
+%{_mandir}/man8/*
+
+%{_sharedir}/kamailio/dbtext/kamailio/*
+
+
+%files mysql
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.db_mysql
+%{_libdir}/kamailio/modules/db_mysql.so
+%{_libdir}/kamailio/kamctl/kamctl.mysql
+%{_libdir}/kamailio/kamctl/kamdbctl.mysql
+%{_sharedir}/kamailio/mysql/*
+
+
+%files postgresql
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.db_postgres
+%{_libdir}/kamailio/modules/db_postgres.so
+%{_libdir}/kamailio/kamctl/kamctl.pgsql
+%{_libdir}/kamailio/kamctl/kamdbctl.pgsql
+%{_sharedir}/kamailio/postgres/*
+
+
+%files unixODBC
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules_k/README.db_unixodbc
+%{_libdir}/kamailio/modules_k/db_unixodbc.so
+
+
+%files bdb
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.db_berkeley
+%{_sbindir}/kambdb_recover
+%{_libdir}/kamailio/modules/db_berkeley.so
+%{_libdir}/kamailio/kamctl/kamctl.db_berkeley
+%{_libdir}/kamailio/kamctl/kamdbctl.db_berkeley
+%{_sharedir}/kamailio/db_berkeley/*
+
+
+%files sqlite
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules_k/README.db_sqlite
+%{_libdir}/kamailio/modules_k/db_sqlite.so
+%{_libdir}/kamailio/kamctl/kamctl.sqlite
+%{_libdir}/kamailio/kamctl/kamdbctl.sqlite
+%{_sharedir}/kamailio/db_sqlite/*
+
+
+%files utils
+%defattr(-,root,root)
+%{_docdir}/kamailio/modules/README.utils
+%{_libdir}/kamailio/modules/utils.so
+
+
+%files cpl
+%defattr(-,root,root)
+%{_docdir}/kamailio/modules_k/README.cpl-c
+%{_libdir}/kamailio/modules_k/cpl-c.so
+
+
+%files radius
+%defattr(-,root,root)
+%{_docdir}/kamailio/modules_k/README.acc_radius
+%{_docdir}/kamailio/modules_k/README.auth_radius
+%{_docdir}/kamailio/modules_k/README.misc_radius
+%{_docdir}/kamailio/modules/README.peering
+%{_libdir}/kamailio/modules_k/acc_radius.so
+%{_libdir}/kamailio/modules_k/auth_radius.so
+%{_libdir}/kamailio/modules_k/misc_radius.so
+%{_libdir}/kamailio/modules/peering.so
+
+
+%files snmpstats
+%defattr(-,root,root)
+%{_docdir}/kamailio/modules_k/README.snmpstats
+%{_libdir}/kamailio/modules_k/snmpstats.so
+
+
+%files presence
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules_k/README.presence
+%doc %{_docdir}/kamailio/modules_k/README.presence_conference
+%doc %{_docdir}/kamailio/modules_k/README.presence_dialoginfo
+%doc %{_docdir}/kamailio/modules_k/README.presence_mwi
+%doc %{_docdir}/kamailio/modules_k/README.presence_profile
+%doc %{_docdir}/kamailio/modules_k/README.presence_reginfo
+%doc %{_docdir}/kamailio/modules_k/README.presence_xml
+%doc %{_docdir}/kamailio/modules_k/README.pua
+%doc %{_docdir}/kamailio/modules_k/README.pua_bla
+%doc %{_docdir}/kamailio/modules_k/README.pua_dialoginfo
+%doc %{_docdir}/kamailio/modules_k/README.pua_mi
+%doc %{_docdir}/kamailio/modules_k/README.pua_reginfo
+%doc %{_docdir}/kamailio/modules_k/README.pua_usrloc
+%doc %{_docdir}/kamailio/modules_k/README.pua_xmpp
+%doc %{_docdir}/kamailio/modules_k/README.rls
+%doc %{_docdir}/kamailio/modules_k/README.xcap_client
+%doc %{_docdir}/kamailio/modules_k/README.xcap_server
+%{_libdir}/kamailio/modules_k/presence.so
+%{_libdir}/kamailio/modules_k/presence_conference.so
+%{_libdir}/kamailio/modules_k/presence_dialoginfo.so
+%{_libdir}/kamailio/modules_k/presence_mwi.so
+%{_libdir}/kamailio/modules_k/presence_profile.so
+%{_libdir}/kamailio/modules_k/presence_reginfo.so
+%{_libdir}/kamailio/modules_k/presence_xml.so
+%{_libdir}/kamailio/modules_k/pua.so
+%{_libdir}/kamailio/modules_k/pua_bla.so
+%{_libdir}/kamailio/modules_k/pua_dialoginfo.so
+%{_libdir}/kamailio/modules_k/pua_mi.so
+%{_libdir}/kamailio/modules_k/pua_reginfo.so
+%{_libdir}/kamailio/modules_k/pua_usrloc.so
+%{_libdir}/kamailio/modules_k/pua_xmpp.so
+%{_libdir}/kamailio/modules_k/rls.so
+%{_libdir}/kamailio/modules_k/xcap_client.so
+%{_libdir}/kamailio/modules_k/xcap_server.so
+
+
+%files xmpp
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules_k/README.xmpp
+%{_libdir}/kamailio/modules_k/xmpp.so
+
+
+%files tls
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.tls
+%{_libdir}/kamailio/modules/tls.so
+
+
+%files carrierroute
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.carrierroute
+%{_libdir}/kamailio/modules/carrierroute.so
+
+
+%files purple
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules_k/README.purple
+%{_libdir}/kamailio/modules_k/purple.so
+
+
+%files ldap
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules_k/README.h350
+%doc %{_docdir}/kamailio/modules_k/README.ldap
+%{_libdir}/kamailio/modules_k/h350.so
+%{_libdir}/kamailio/modules_k/ldap.so
+
+
+%files xmlrpc
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.xmlrpc
+%{_libdir}/kamailio/modules/xmlrpc.so
+
+
+%files perl
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules_k/README.perl
+%doc %{_docdir}/kamailio/modules_k/README.perlvdb
+%{_libdir}/kamailio/modules_k/perl.so
+%{_libdir}/kamailio/modules_k/perlvdb.so
+%{_libdir}/kamailio/perl/OpenSER.pm
+%{_libdir}/kamailio/perl/OpenSER/Constants.pm
+%{_libdir}/kamailio/perl/OpenSER/LDAPUtils/LDAPConf.pm
+%{_libdir}/kamailio/perl/OpenSER/LDAPUtils/LDAPConnection.pm
+%{_libdir}/kamailio/perl/OpenSER/Message.pm
+%{_libdir}/kamailio/perl/OpenSER/Utils/Debug.pm
+%{_libdir}/kamailio/perl/OpenSER/Utils/PhoneNumbers.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Adapter/AccountingSIPtrace.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Adapter/Alias.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Adapter/Auth.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Adapter/Describe.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Adapter/Speeddial.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Adapter/TableVersions.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Column.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Pair.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/ReqCond.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Result.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/VTab.pm
+%{_libdir}/kamailio/perl/OpenSER/VDB/Value.pm
+
+
+%files lua
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.app_lua
+%{_libdir}/kamailio/modules/app_lua.so
+
+
+%files python
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.app_python
+%{_libdir}/kamailio/modules/app_python.so
+
+
+%files GeoIP
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.geoip
+%{_libdir}/kamailio/modules/geoip.so
+
+
+%files regex
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules_k/README.regex
+%{_libdir}/kamailio/modules_k/regex.so
+
+
+%files dialplan
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.dialplan
+%{_libdir}/kamailio/modules/dialplan.so
+
+
+%files lcr
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.lcr
+%{_libdir}/kamailio/modules/lcr.so
+
+
+%files xmlops
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.xmlops
+%{_libdir}/kamailio/modules/xmlops.so
+
+
+%files redis
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.ndb_redis
+%{_libdir}/kamailio/modules/ndb_redis.so
+
+
+%files json
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.json
+%doc %{_docdir}/kamailio/modules/README.jsonrpc-c
+%{_libdir}/kamailio/modules/json.so
+%{_libdir}/kamailio/modules/jsonrpc-c.so
+
+
+%files mono
+%defattr(-,root,root)
+%doc %{_docdir}/kamailio/modules/README.app_mono
+%{_libdir}/kamailio/modules/app_mono.so
+
+
+
+%changelog
+* Mon May 7 2012 Peter Dunkley <[email protected]>
+  - Changed to use systemd instead of SysV init.
+* Sun May 6 2012 Peter Dunkley <[email protected]>
+  - First version created for Kamailio 3.3.0. Based on spec-file for CentOS
+    created by Ovidiu Sas.
+  - Tested with Fedora 16 x86_64.
+  - Builds all Kamailio 3.3.0 modules (modules/modules_k) except:
+    - modules/auth_identity: Conflicts with TLS unless statically linked (which
+      requires changes to Makefile and is impractical for generic RPM building)
+    - modules/db_cassandra: Requires Thrift which is not in the F16 repo
+    - modules/iptrtpproxy: Needs local copy of iptables source to build
+      (impractical for generic RPM building)
+    - modules_k/db_oracle: Requires Oracle which is not in the F16 repo
+      (and is closed-source)
+    - modules_k/memcached: Module compilation appears to require an older
+      version of libmemcached-devel than the one in the F16 repo
+    - modules_k/mi_xmlrpc: The F16 repo contains an unsupported version of
+      libxmlrpc-c3, and there is an compilation error due to the module code
+      using an unknown type ('TString')
+    - modules_k/osp: Requires OSP Toolkit which is not in the F16 repo

+ 14 - 0
pkg/kamailio/fedora/kamailio.sysconfig

@@ -0,0 +1,14 @@
+#
+# Kamailio startup options
+#
+
+# Options to pass when starting Kamailio
+# Note: variable interpolation is not supported by systemd so $OPTIONS cannot
+#       be constructed from multiple variables.
+# -P - The PID file (this must match the value in
+#      /lib/systemd/system/kamailio.service)
+# -m - Kamailio shared (global) memory (default 32 MB)
+# -M - Kamailio package (per-process private) memory (default 4 MB)
+# -u - User to run Kamailio as
+# -g - Group to run Kamailio as
+OPTIONS="-P /var/run/kamailio.pid -m 32 -M 4 -u kamailio -g kamailio"

+ 91 - 9
utils/kamctl/kamctl

@@ -1051,9 +1051,9 @@ domain() {
 
 
 #
 #
 ##### ------------------------------------------------ #####
 ##### ------------------------------------------------ #####
-### trusted management
+### permissions trusted management
 #
 #
-trusted() {
+permissions_trusted() {
 	case $1 in
 	case $1 in
 		reload)
 		reload)
 			require_ctlengine
 			require_ctlengine
@@ -1123,6 +1123,78 @@ trusted() {
 	esac
 	esac
 }
 }
 
 
+
+#
+##### ------------------------------------------------ #####
+### permissions address management
+#
+permissions_address() {
+	case $1 in
+		reload)
+			require_ctlengine
+			$CTLCMD address_reload
+			;;
+		dump)
+			require_ctlengine
+			$CTLCMD address_dump
+			;;
+		show)
+			require_dbengine
+			QUERY="select * FROM $ADDRESS_TABLE ; "
+			$DBROCMD "$QUERY"
+			;;
+		add)
+			require_dbengine
+			shift
+			if [ $# -lt 2 ] ; then
+				usage_address
+				exit 1
+			fi
+			AMASK=32
+			if [ ! -z "$3" ]; then
+				AMASK="$3"
+			fi
+			APORT=0
+			if [ ! -z "$4" ]; then
+				APORT="$4"
+			fi
+			ATAG=""
+			if [ ! -z "$5" ]; then
+				ATAG="$5"
+			fi
+
+			QUERY="insert into $ADDRESS_TABLE \
+				(grp, ip_addr, mask, port, tag) \
+				VALUES ($1, '$2', $AMASK, $APORT, '$ATAG');"
+			$DBCMD "$QUERY"
+			if [ $? -ne 0 ] ; then
+				merr "permissions address - SQL Error"
+				exit 1
+			fi
+			minfo "execute '$0 address reload' to synchronize cache and database"
+			;;
+		rm)
+			require_dbengine
+			shift
+			if [ $# -ne 2 ] ; then
+				usage_address
+				exit 1
+			fi
+			QUERY="delete from $ADDRESS_TABLE where grp=$1 and ip_addr='$2';"
+			$DBCMD "$QUERY"
+			if [ $? -ne 0 ] ; then
+				merr "permissions address - SQL Error"
+				exit 1
+			fi
+			minfo "execute '$0 address reload' to synchronize cache and database"
+			;;
+		*)
+			usage_address
+			exit 1
+	esac
+}
+
+
 #
 #
 ##### ------------------------------------------------ #####
 ##### ------------------------------------------------ #####
 ### LCR management
 ### LCR management
@@ -2402,10 +2474,15 @@ case $1 in
 
 
 	trusted)
 	trusted)
 		shift
 		shift
-		trusted "$@"
+		permissions_trusted "$@"
+		;;
+
+	address)
+		shift
+		permissions_address "$@"
 		;;
 		;;
 
 
-	fifo|unixsock|ser_mi|sercmd_mi|sercmdmi)
+	fifo|mi|unixsock|ser_mi|sercmd_mi|sercmdmi)
 		require_ctlengine
 		require_ctlengine
 		shift
 		shift
 		$CTLCMD "$@"
 		$CTLCMD "$@"
@@ -2427,11 +2504,6 @@ case $1 in
 		cr "$@"
 		cr "$@"
 		;;
 		;;
 
 
-	trusted)
-		shift
-		trusted "$@"
-		;;
-
 	dispatcher)
 	dispatcher)
 		shift
 		shift
 		dispatcher "$@"
 		dispatcher "$@"
@@ -2469,6 +2541,16 @@ case $1 in
 		$CTLCMD ps
 		$CTLCMD ps
 		;;
 		;;
 
 
+	uptime)
+		require_ctlengine
+		$CTLCMD uptime
+		;;
+
+	stats)
+		require_ctlengine
+		$CTLCMD get_statistics all
+		;;
+
 	restart)
 	restart)
 		openser_stop
 		openser_stop
 		sleep 2
 		sleep 2

+ 22 - 6
utils/kamctl/kamctl.base

@@ -180,11 +180,6 @@ fi
 DO_DOMAIN_COLUMN=domain
 DO_DOMAIN_COLUMN=domain
 DO_LAST_MODIFIED_COLUMN=last_modified
 DO_LAST_MODIFIED_COLUMN=last_modified
 
 
-# trusted table
-if [ -z "$TRUSTED_TABLE" ] ; then
-	TRUSTED_TABLE=trusted
-fi
-
 # lcr tables
 # lcr tables
 if [ -z "$LCR_TABLE" ] ; then
 if [ -z "$LCR_TABLE" ] ; then
 	LCR_TABLE=lcr
 	LCR_TABLE=lcr
@@ -279,6 +274,11 @@ TRUSTED_PROTO_COLUMN=proto
 TRUSTED_FROM_PATTERN_COLUMN=from_pattern
 TRUSTED_FROM_PATTERN_COLUMN=from_pattern
 TRUSTED_TAG_COLUMN=tag
 TRUSTED_TAG_COLUMN=tag
 
 
+# address table
+if [ -z "$ADDRESS_TABLE" ] ; then
+	ADDRESS_TABLE=address
+fi
+
 # dispatcher tables  
 # dispatcher tables  
 if [ -z "$DISPATCHER_TABLE" ] ; then
 if [ -z "$DISPATCHER_TABLE" ] ; then
 	DISPATCHER_TABLE=dispatcher
 	DISPATCHER_TABLE=dispatcher
@@ -416,7 +416,23 @@ cat <<EOF
  trusted add <src_ip> <proto> <from_pattern> <tag>
  trusted add <src_ip> <proto> <from_pattern> <tag>
              ....................... add a new entry
              ....................... add a new entry
 	     ....................... (from_pattern and tag are optional arguments)
 	     ....................... (from_pattern and tag are optional arguments)
- trusted rm <src_ip> ............... remove all entres for the given src_ip
+ trusted rm <src_ip> ............... remove all entries for the given src_ip
+EOF
+}
+USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_trusted"
+
+usage_address() {
+	echo
+	mecho " -- command 'add|dump|reload|rm|show' - manage address"
+	echo
+cat <<EOF
+ address show ...................... show db content
+ address dump ...................... show cache content
+ address reload .................... reload db table into cache
+ address add <grp> <ipaddr> <mask> <port> <tag>
+             ....................... add a new entry
+	     ....................... (mask, port and tag are optional arguments)
+ address rm <grp> <ipaddr> ......... remove entries for given grp and ipaddr
 EOF
 EOF
 }
 }
 USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_trusted"
 USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_trusted"

+ 26 - 0
utils/kamctl/kamctl.ctlbase

@@ -5,6 +5,10 @@
 #
 #
 #===================================================================
 #===================================================================
 
 
+[ "${IMPCTLBASE}" = "yes" ] && return
+
+export IMPCTLBASE="yes"
+
 ##### ----------------------------------------------- #####
 ##### ----------------------------------------------- #####
 ### common variables and functions for CTL engines
 ### common variables and functions for CTL engines
 #
 #
@@ -72,3 +76,25 @@ EOF
 }
 }
 USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_usrloc"
 USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_usrloc"
 
 
+usage_ctlcommon() {
+	echo
+	mecho " -- command 'ps' - print details about running processes"
+	echo
+cat <<EOF
+ ps ................................. details about running processes
+EOF
+	echo
+	mecho " -- command 'uptime' - print uptime details"
+	echo
+cat <<EOF
+ uptime ............................. print start time end elapsed seconds
+EOF
+	echo
+	mecho " -- command 'stats' - print internal statistics"
+	echo
+cat <<EOF
+ stats .............................. dump all internall statistics
+EOF
+}
+USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_ctlcommon"
+

+ 3 - 2
utils/kamctl/kamctl.fifo

@@ -33,10 +33,11 @@ fi
 #
 #
 usage_fifo() {
 usage_fifo() {
 	echo
 	echo
-	mecho " -- command 'fifo'"
+	mecho " -- command 'mi' - send raw MI commands"
 	echo
 	echo
 cat <<EOF
 cat <<EOF
- fifo ............................... send raw FIFO command
+ mi ................................. send raw MI command
+ fifo ............................... send raw FIFO (MI) command
 EOF
 EOF
 }
 }
 USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_fifo"
 USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_fifo"