Forráskód Böngészése

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

Forgot to pull before commit.
Juha Heinanen 13 éve
szülő
commit
1d23563dd6
43 módosított fájl, 3125 hozzáadás és 532 törlés
  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 (OpenSER) SIP Server v3.2 - default configuration script
+# Kamailio (OpenSER) SIP Server v3.3 - default configuration script
 #     - web: http://www.kamailio.org
 #     - git: http://sip-router.org
 #
@@ -306,6 +306,8 @@ modparam("registrar", "method_filtering", 1)
 #modparam("registrar", "max_contacts", 10)
 # max value for expires of registrations
 modparam("registrar", "max_expires", 3600)
+# set it to 1 to enable GRUU
+modparam("registrar", "gruu_enabled", 0)
 
 
 # ----- acc params -----

+ 1 - 1
lib/srdb1/db_op.h

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

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

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

+ 0 - 1
modules/auth_identity/auth_http.c

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

+ 0 - 1
modules/auth_identity/auth_identity.c

@@ -47,7 +47,6 @@
 #include <openssl/sha.h>
 
 #include <curl/curl.h>
-#include <curl/types.h>
 #include <curl/easy.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)
 {
-	int i;
+	int i, retries;
 	ExecStatusType pqresult;
 
 	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;
 	}
 
-	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 */
 		db_postgres_free_query(_con);
 		/* 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;
 
-	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
-	if (db_postgres_store_result(_h, &_r) != 0)
+	int tmp = db_postgres_store_result(_h, &_r);
+
+	if (tmp < 0) {
 		LM_WARN("unexpected result returned");
+		ret = tmp;
+	}
 	
 	if (_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)
 {
 	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);
+	int tmp = db_postgres_store_result(_h, &_r);
 
-	if (db_postgres_store_result(_h, &_r) != 0)
+	if (tmp < 0) {
 		LM_WARN("unexpected result returned");
-	
+		ret = tmp;
+	}
+
 	if (_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)
 {
 	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);
+	int tmp = db_postgres_store_result(_h, &_r);
 
-	if (db_postgres_store_result(_h, &_r) != 0)
+	if (tmp < 0) {
 		LM_WARN("unexpected result returned");
+		ret = tmp;
+	}
 	
 	if (_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_DESTROY         (1<<13)
 #define DLGCB_SPIRALED        (1<<14)
-#define DLGCB_TERMINATED_CONFIRMED (1<<14)
+#define DLGCB_TERMINATED_CONFIRMED (1<<15)
 
 struct dlg_callback {
 	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 *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);
 		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;
-		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;
 		if(m_build_headers(&hdr_str, str_vals[3] /*ctype*/,
 				   str_vals[0]/*from*/, rtime /*Date*/,

+ 156 - 90
modules_k/presence/README

@@ -41,19 +41,22 @@ Juha Heinanen
               3.6. xcap_table(str)
               3.7. clean_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
 
@@ -101,25 +104,28 @@ Juha Heinanen
    1.6. Set xcap_table parameter
    1.7. Set clean_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
 
 Chapter 1. Admin Guide
@@ -142,19 +148,22 @@ Chapter 1. Admin Guide
         3.6. xcap_table(str)
         3.7. clean_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
 
@@ -224,19 +233,22 @@ Chapter 1. Admin Guide
    3.6. xcap_table(str)
    3.7. clean_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)
 
@@ -341,19 +353,73 @@ modparam("presence", "clean_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
    SUBSCRIBE requests.
 
    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')
 ...
 
-3.10. expires_offset (int)
+3.13. expires_offset (int)
 
    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
@@ -361,35 +427,35 @@ modparam("presence", "to_tag_pref", 'pres')
 
    Default value is “0”.
 
-   Example 1.10. Set expires_offset parameter
+   Example 1.13. Set expires_offset parameter
 ...
 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
    (in seconds).
 
    Default value is “3600”.
 
-   Example 1.11. Set max_expires parameter
+   Example 1.14. Set max_expires parameter
 ...
 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
    header filed for 200 OK replies to SUBSCRIBE and PUBLISH and in NOTIFY
    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")
 ...
 
-3.13. subs_db_mode (int)
+3.16. subs_db_mode (int)
 
    The presence module can utilize database for persistent subscription
    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).
 
-   Example 1.13. Set subs_db_mode parameter
+   Example 1.16. Set subs_db_mode parameter
 ...
 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
    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”.
 
-   Example 1.14. Set publ_cache parameter
+   Example 1.17. Set publ_cache parameter
 ...
 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.
    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)”.
 
-   Example 1.15. Set subs_htable_size parameter
+   Example 1.18. Set subs_htable_size parameter
 ...
 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
    parameter will be used as the power of 2 when computing table size.
 
    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)
 ...
 
-3.17. send_fast_notify (int)
+3.20. send_fast_notify (int)
 
    This parameter enables or disables the sending of an initial empty
    NOTIFY after a SUBSCRIBE/reSUBSCRIBE. This caused problems for MWI
@@ -484,12 +550,12 @@ modparam("presence", "pres_htable_size", 11)
 
    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)
 ...
 
-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
    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 ”.
 
-   Example 1.18. Set enable_sphere_check parameter
+   Example 1.21. Set enable_sphere_check parameter
 ...
 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
    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”.
 
-   Example 1.19. Set timeout_rm_subs parameter
+   Example 1.22. Set timeout_rm_subs parameter
 ...
 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.
 
    Default value is 500.
 
-   Example 1.20. Set fetch_rows parameter
+   Example 1.23. Set fetch_rows parameter
 ...
 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
    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.
 
-   Example 1.21. Set integrated_xcap_server parameter
+   Example 1.24. Set integrated_xcap_server parameter
 ...
 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.
 
-   Example 1.22. handle_publish usage
+   Example 1.25. handle_publish usage
 ...
         if(is_method("PUBLISH"))
         {
@@ -604,7 +670,7 @@ modparam("presence", "integrated_xcap_server", 1)
 
    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")
     handle_subscribe();
@@ -621,7 +687,7 @@ if(method=="SUBSCRIBE")
 
    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") {
     pres_auth_status("$fu", $ru");
@@ -652,7 +718,7 @@ if (method=="MESSAGE") {
 
    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);
 ...
@@ -670,7 +736,7 @@ pres_refresh_watchers("sip:[email protected]", "presence", 1);
 
    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");
 ...
@@ -689,7 +755,7 @@ pres_update_watchers("sip:[email protected]", "presence");
 
    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
 =>file)");

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

@@ -239,7 +239,90 @@ modparam("presence", "db_update_period", 100)
 		</example>
 	</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>
 		<para>
 		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_winfo_col = str_init("updated_winfo");
 
+int subset=0;
+
 char* get_status_str(int status_flag)
 {
 	switch(status_flag)
@@ -111,22 +113,24 @@ char* get_status_str(int status_flag)
 }
 
 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)
@@ -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)
 {	
 	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;
 	db_row_t *row = 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;
 	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[expires_col=n_result_cols++] = &str_expires_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;
 		pkg_free(w);
 	}
+
+	watchers = NULL;
 }
 
 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_notify_body(notify_body, p->event);	
 	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 )
 {
@@ -1351,14 +1435,14 @@ int send_notify_request(subs_t* subs, subs_t * watcher_subs,
 	str str_hdr = {0, 0};
 	str* notify_body = NULL;
 	int result= 0;
-    c_back_param *cb_param= NULL;
+	c_back_param *cb_param= NULL;
 	str* final_body= NULL;
 	uac_req_t uac_r;
 	
 	LM_DBG("dialog info:\n");
 	printf_subs(subs);
 
-    /* getting the status of the subscription */
+	/* getting the status of the subscription */
 
 	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(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");
 			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,
 		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);
 
@@ -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);
 void free_notify_body(str *body, pres_ev_t *ev);
+void pres_timer_send_notify(unsigned int ticks, void *param);
 #endif

+ 92 - 24
modules_k/presence/presence.c

@@ -72,6 +72,7 @@
 #include "bind_presence.h"
 #include "notify.h"
 #include "../../mod_fix.h"
+#include "../../timer_proc.h"
 
 MODULE_VERSION
 
@@ -145,8 +146,13 @@ int sphere_enable= 0;
 int timeout_rm_subs = 1;
 int send_fast_notify = 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_notifier_id = NULL;
+
 int phtable_size= 9;
 phtable_t* pres_htable=NULL;
 
@@ -182,6 +188,9 @@ static param_export_t params[]={
 	{ "xcap_table",             STR_PARAM, &pres_xcap_table.s},
 	{ "clean_period",           INT_PARAM, &clean_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 },
 	{ "expires_offset",         INT_PARAM, &expires_offset },
 	{ "max_expires",            INT_PARAM, &max_expires },
@@ -416,6 +425,26 @@ static int mod_init(void)
 	if(db_update_period>0)
 		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_db = NULL;
 
@@ -433,8 +462,31 @@ static int mod_init(void)
  */
 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();
 	
@@ -593,6 +645,9 @@ static void destroy(void)
 	if(pres_xcap_db && pres_xcap_dbf.close)
 		pres_xcap_dbf.close(pres_xcap_db);
 
+	if (pres_notifier_id != NULL)
+		shm_free(pres_notifier_id);
+
 	destroy_evlist();
 }
 
@@ -1005,14 +1060,14 @@ int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc)
 		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);
 	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)
 	{
-        n= result->n;
+        	n= result->n;
 		ws_list= (ws_t*)pkg_malloc(n * sizeof(ws_t));
 		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");
 			goto done;
 		}
-    }
+	}
 
 	pa_dbf.free_result(pa_db, result);
 	result= NULL;
 
 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;
 			}
-		}
 
-		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);
@@ -1191,8 +1249,8 @@ done:
 
 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];
 	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;
@@ -1384,7 +1442,7 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 
 	pa_dbf.free_result(pa_db, result);
 
-	if (subs->status == TERMINATED_STATUS)
+	if (pres_notifier_processes == 0 && subs->status == TERMINATED_STATUS)
 	{
 		/* delete the records */
 		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;
 	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,
 				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 */
 #define DB_ONLY          3
 
+#define NO_UPDATE_TYPE	-1
+#define UPDATED_TYPE	1
+
 /** TM bind */
 extern struct tm_binds tmb;
 
@@ -84,6 +87,10 @@ extern int send_fast_notify;
 extern int shtable_size;
 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 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_412_rpl  = str_init("Conditional request failed");
 
+static str str_offline_etag_val = str_init("*#-OFFLINE-#*");
+
 extern int pres_fetch_rows;
 
 #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 ret = -1;
 	int db_record_exists = 0;
+	int num_watchers = 0;
 
 	if (sent_reply) *sent_reply= 0;
-	if(presentity->event->req_auth)
+	if(pres_notifier_processes == 0 && presentity->event->req_auth)
 	{
 		/* get rules_document */
 		if(presentity->event->get_rules_doc(&presentity->user,
@@ -531,26 +534,50 @@ after_dialog_check:
 				goto error;
 			}
 			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 */
 			if( publ_cache_enabled &&
@@ -730,10 +757,21 @@ after_dialog_check:
 send_notify:
 
 	/* 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:
@@ -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);
 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
 

+ 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;
 	int n_result_cols= 0;
 	str* rules_doc= NULL;
-
+	int num_watchers = 0;
 
 	LM_DBG("cleaning expired presentity information\n");
 	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",
 			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;
 		pkg_free(p[i].p);
@@ -247,8 +270,11 @@ void msg_presentity_clean(unsigned int ticks,void *param)
 	}
 
 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;
 
@@ -579,6 +605,12 @@ static int fetch_presentity(str furi, str *presentity)
 		return -1;
 	}
 
+	if (result == NULL)
+	{
+		LM_ERR("bad result\n");
+		return -1;
+	}
+
 	if (result->n <=0)
 	{
 		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[reason_col].val.str_val= s->reason;
 	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)
 	{
@@ -358,8 +358,8 @@ int insert_subs_db(subs_t* s, 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_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].val.int_val = subs->remote_cseq; 
 		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)
 	{
@@ -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].val.str_val = subs->reason;
 	n_update_cols++;
-	
+
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
 	{
 		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");
 }
 
+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* 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(to_tag_gen)
 	{
+		subs.updated = NO_UPDATE_TYPE;
+		subs.updated_winfo = NO_UPDATE_TYPE;
+
 		if(!event->req_auth) 
 			subs.status = ACTIVE_STATUS;
 		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), 
             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");
 		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_val_t query_vals[3];
-	db_key_t result_cols[7];
+	db_key_t result_cols[9];
 	db1_res_t *result= NULL;
 	db_row_t *row ;	
 	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 remote_cseq_col= 0, local_cseq_col= 0, status_col, reason_col;
 	int record_route_col, version_col, pres_uri_col;
+	int updated_col, updated_winfo_col;
 	unsigned int remote_cseq;
 	str pres_uri, record_route;
 	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[record_route_col=n_result_cols++] = &str_record_route_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) 
 	{
@@ -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->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);
 	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)
 {
 	db_op_t qops[1];
@@ -1992,17 +2146,22 @@ void timer_db_update(unsigned int ticks,void *param)
 
 
 	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);
 	}
 }
@@ -2072,6 +2231,11 @@ int restore_db_subs(void)
 		goto error;
 	}
 
+	if (result == NULL)
+	{
+		LM_ERR("bad result\n");
+		goto error;
+	}
 
 	do {
 		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)
 {
 	db_key_t db_keys[5];

+ 1 - 5
modules_k/presence/subscribe.h

@@ -85,6 +85,7 @@ struct subscription
 	int recv_event;
 	int internal_update_flag;
 	int updated;
+	int updated_winfo;
 	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 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);
 
 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);
 	}
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(-1);
+	}
+
 	if (RES_ROW_N(res) == 0)
 	{
 		/* no match */ 
@@ -285,6 +291,12 @@ int is_dialog_puadb(ua_pres_t *pres)
 		return(-1);
 	}
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(-1);
+	}
+
 	nr_rows = RES_ROW_N(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);
 	}
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(-1);
+	}
+
 	nr_rows = RES_ROW_N(res);
 
 	switch (nr_rows)
@@ -392,6 +410,12 @@ int get_record_id_puadb(ua_pres_t *pres, str **rec_id )
 			return(-1);
 		}
 
+		if (res == NULL)
+		{
+			LM_ERR("bad result\n");
+			return(-1);
+		}
+
 		nr_rows = RES_ROW_N(res);
 
 		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);
 	}
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(NULL);
+	}
+
 	nr_rows = RES_ROW_N(res);
 
 	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);
 	}
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return(NULL);
+	}
+
 	nr_rows = RES_ROW_N(res);
 
 	if (nr_rows == 0)
@@ -1524,6 +1560,12 @@ list_entry_t *get_subs_list_puadb(str *did)
 		return list;
 	}
 
+	if (res == NULL)
+	{
+		LM_ERR("bad result\n");
+		return list;
+	}
+
 	if (RES_ROW_N(res) == 0)
 	{
 		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)
 			&& (RES_ROWS(res)>0));
 
+	pua_dbf.free_result(pua_db, res);
+	
 	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");
 			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 {
 		uri.len = record->aor.len + 6;
 		uri.s = (char*)pkg_malloc(sizeof(char) * uri.len);

+ 12 - 4
modules_k/purple/Makefile

@@ -1,12 +1,20 @@
 include ../../Makefile.defs
 auto_gen=
 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
-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
 

+ 1 - 0
modules_k/purple/purple.h

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

+ 1 - 0
modules_k/purple/purplepipe.h

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

+ 1 - 1
modules_k/purple/utils.h

@@ -21,7 +21,7 @@
 #define _UTILS_H
 
 #include <stdlib.h>
-
+#include <glib.h>
 #include <libpurple/savedstatuses.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
    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”
 
    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>
 		The module supports 2 modes of operation, high speed memory
 		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>
 		<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[auth_state_col= n_result_cols++]= &str_auth_state_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) 
 	{
@@ -143,8 +148,30 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, str* rl_uri,
 		LM_ERR("in sql query\n");
 		goto error;
 	}
-	if(result== NULL)
+	if(result == NULL)
+	{
+		LM_ERR("bad result\n");
 		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.
 	 * 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));
 	xmlFreeDoc(rlmi_body);
 
-	rlpres_dbf.free_result(rlpres_db, result);
-	result= NULL;
-
 	if(agg_body_sendn_update(rl_uri, boundary_string, rlmi_cont,
 		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;
 	}
 
-	/* 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);
 	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;
 	pkg_free(rlsubs_did.s);
-
+	rlpres_dbf.free_result(rlpres_db, result);
+	
 	return 0;
 error:
 	if(rlmi_cont)
@@ -1325,6 +1323,12 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		return -1;
 	}
 
+	if(result == NULL)
+	{
+		LM_ERR("bad result\n");
+		return -1;
+	}
+
 	if(result->n<=0)
 	{
 		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;
 	db1_res_t *result = NULL;
 	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;
 	str ev_sname;
 	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].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_vals[0].type = DB1_INT;
@@ -936,23 +941,23 @@ static void timer_send_full_state_notifies(int round)
 	{
 		memset(&sub, 0, sizeof(subs_t));
 		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);
 		if (sub.event == NULL)
 		{
@@ -960,12 +965,12 @@ static void timer_send_full_state_notifies(int round)
 			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
 			sub.expires = 0;
 
@@ -1063,10 +1068,9 @@ static void timer_send_update_notifies(int round)
 		LM_ERR("in sql query\n");
 		goto done;
 	}
-	if(result== NULL || result->n<= 0)
+	if(result == NULL || result->n <= 0)
 		goto done;
 
-	/* update the rlpres table */
 	if(rlpres_dbf.update(rlpres_db, query_cols, 0, query_vals, update_cols,
 					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_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, 
 				n_query_cols, n_result_cols, 0, &result )< 0)
 	{
@@ -221,28 +212,12 @@ int delete_expired_subs_rlsdb( void )
 		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;
 
 error:
 	if (result) rls_dbf.free_result(rls_db, result);
 	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;
 }
 
@@ -739,6 +714,7 @@ int get_dialog_subscribe_rlsdb(subs_t *subs)
 	db_key_t result_cols[5];
 	db_row_t *rows;
 	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 r_remote_cseq, r_local_cseq, r_version;
 	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;
 	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, 
 			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);
 	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)
 	{

+ 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;
 	}
 
+	if (result == NULL)
+	{
+		LM_ERR("bad result\n");
+		return -1;
+	}
+
 	if(result->n<=0)
 	{
 		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)/srdb1/srdb1
 SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/srutils/srutils
 include ../../Makefile.modules

+ 9 - 1
modules_k/usrloc/ul_mi.c

@@ -35,6 +35,7 @@
 #include <string.h>
 #include <stdio.h>
 #include "../../lib/kmi/mi.h"
+#include "../../lib/srutils/sruid.h"
 #include "../../dprint.h"
 #include "../../ut.h"
 #include "../../qvalue.h"
@@ -49,12 +50,14 @@
 /*! CSEQ nr used */
 #define MI_UL_CSEQ 1
 /*! 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 */
 static str mi_ul_ua  = str_init("SIP Router MI Server");
 /*! path used for ul_add and ul_rm_contact */
 static str mi_ul_path = str_init("dummypath");
 
+extern sruid_t _ul_sruid;
+
 /************************ 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)
 		goto bad_syntax;
 
+	if(sruid_next(&_ul_sruid)<0)
+		goto error;
+	ci.ruid = _ul_sruid.uid;
+
 	lock_udomain( dom, aor);
 
 	n = get_urecord( dom, aor, &r);
@@ -580,6 +587,7 @@ release_error:
 	release_urecord(r);
 lock_error:
 	unlock_udomain( dom, aor);
+error:
 	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 "../../globals.h"   /* is_main */
 #include "../../ut.h"        /* str_init */
+#include "../../lib/srutils/sruid.h"
 #include "dlist.h"           /* register_udomain */
 #include "udomain.h"         /* {insert,delete,get,release}_urecord */
 #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_timer_procs = 0;
 
+/* sruid to get internal uid for mi/rpc commands */
+sruid_t _ul_sruid;
+
 /*
  * Module parameters and their default values
  */
@@ -241,6 +245,9 @@ static int mod_init(void)
 	int i;
 	udomain_t* d;
 
+	if(sruid_init(&_ul_sruid, '-', "ulcx", SRUID_INC)<0)
+		return -1;
+
 #ifdef STATISTICS
 	/* register statistics */
 	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
 		reload)
 			require_ctlengine
@@ -1123,6 +1123,78 @@ trusted() {
 	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
@@ -2402,10 +2474,15 @@ case $1 in
 
 	trusted)
 		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
 		shift
 		$CTLCMD "$@"
@@ -2427,11 +2504,6 @@ case $1 in
 		cr "$@"
 		;;
 
-	trusted)
-		shift
-		trusted "$@"
-		;;
-
 	dispatcher)
 		shift
 		dispatcher "$@"
@@ -2469,6 +2541,16 @@ case $1 in
 		$CTLCMD ps
 		;;
 
+	uptime)
+		require_ctlengine
+		$CTLCMD uptime
+		;;
+
+	stats)
+		require_ctlengine
+		$CTLCMD get_statistics all
+		;;
+
 	restart)
 		openser_stop
 		sleep 2

+ 22 - 6
utils/kamctl/kamctl.base

@@ -180,11 +180,6 @@ fi
 DO_DOMAIN_COLUMN=domain
 DO_LAST_MODIFIED_COLUMN=last_modified
 
-# trusted table
-if [ -z "$TRUSTED_TABLE" ] ; then
-	TRUSTED_TABLE=trusted
-fi
-
 # lcr tables
 if [ -z "$LCR_TABLE" ] ; then
 	LCR_TABLE=lcr
@@ -279,6 +274,11 @@ TRUSTED_PROTO_COLUMN=proto
 TRUSTED_FROM_PATTERN_COLUMN=from_pattern
 TRUSTED_TAG_COLUMN=tag
 
+# address table
+if [ -z "$ADDRESS_TABLE" ] ; then
+	ADDRESS_TABLE=address
+fi
+
 # dispatcher tables  
 if [ -z "$DISPATCHER_TABLE" ] ; then
 	DISPATCHER_TABLE=dispatcher
@@ -416,7 +416,23 @@ cat <<EOF
  trusted add <src_ip> <proto> <from_pattern> <tag>
              ....................... add a new entry
 	     ....................... (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
 }
 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
 #
@@ -72,3 +76,25 @@ EOF
 }
 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() {
 	echo
-	mecho " -- command 'fifo'"
+	mecho " -- command 'mi' - send raw MI commands"
 	echo
 cat <<EOF
- fifo ............................... send raw FIFO command
+ mi ................................. send raw MI command
+ fifo ............................... send raw FIFO (MI) command
 EOF
 }
 USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_fifo"