Kaynağa Gözat

modules_k/presence: RFC 4827 (presence hard-state) support

- Hard-state presence documents are stored as pidf-manipulation
  documents in the integrated XCAP server.
- When one of these documents is put/deleted/changed it can
  be "published" using the new pres_update_presentity() exported
  function.
- Because the original document is in XCAP a client can download
  it and manipulate it directly.
- Hard-state documents have an expiry time of -1 and never expire
  (the clean function in presence has been updated to make sure of
  this).
- The filename of the document is used as the ETag value in the
  presentity table.  This enables multiple hard-state documents
  (with different filenames) to be uploaded for each subscriber.
- Hard-state is useful for permanently setting an avatar, or an
  out-of-office message, etc.
Peter Dunkley 13 yıl önce
ebeveyn
işleme
37812cef5f

+ 190 - 112
modules_k/presence/README

@@ -34,23 +34,26 @@ Juha Heinanen
         3. Parameters
 
               3.1. db_url(str)
-              3.2. presentity_table(str)
-              3.3. active_watchers_table(str)
-              3.4. watchers_table(str)
-              3.5. clean_period (int)
-              3.6. db_update_period (int)
-              3.7. to_tag_pref (str)
-              3.8. expires_offset (int)
-              3.9. max_expires (int)
-              3.10. server_address (str)
-              3.11. subs_db_mode (int)
-              3.12. publ_cache (int)
-              3.13. subs_htable_size (int)
-              3.14. pres_htable_size (int)
-              3.15. send_fast_notify (int)
-              3.16. enable_sphere_check (int)
-              3.17. timeout_rm_subs (int)
-              3.18. fetch_rows (integer)
+              3.2. xcap_db_url(str)
+              3.3. presentity_table(str)
+              3.4. active_watchers_table(str)
+              3.5. watchers_table(str)
+              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)
 
         4. Functions
 
@@ -59,6 +62,7 @@ Juha Heinanen
               4.3. pres_auth_status(watcher_uri, presentity_uri)
               4.4. pres_refresh_watchers(uri, event, type)
               4.5. pres_update_watchers(uri, event)
+              4.6. pres_update_presentity(uri, file_uri, filename)
 
         5. MI Commands
 
@@ -90,28 +94,32 @@ Juha Heinanen
    List of Examples
 
    1.1. Set db_url parameter
-   1.2. Set presentity_table parameter
-   1.3. Set active_watchers_table parameter
-   1.4. Set watchers_table parameter
-   1.5. Set clean_period parameter
-   1.6. Set db_update_period parameter
-   1.7. Set to_tag_pref parameter
-   1.8. Set expires_offset parameter
-   1.9. Set max_expires parameter
-   1.10. Set server_address parameter
-   1.11. Set subs_db_mode parameter
-   1.12. Set publ_cache parameter
-   1.13. Set subs_htable_size parameter
-   1.14. Set pres_htable_size parameter
-   1.15. Set send_fast_notify parameter
-   1.16. Set enable_sphere_check parameter
-   1.17. Set timeout_rm_subs parameter
-   1.18. Set fetch_rows parameter
-   1.19. handle_publish usage
-   1.20. handle_subscribe usage
-   1.21. pres_auth_status usage
-   1.22. pres_refresh_watchers usage
-   1.23. pres_update_watchers usage
+   1.2. Set xcap_db_url parameter
+   1.3. Set presentity_table parameter
+   1.4. Set active_watchers_table parameter
+   1.5. Set watchers_table parameter
+   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
    2.1. presence_api_t structure
 
 Chapter 1. Admin Guide
@@ -127,23 +135,26 @@ Chapter 1. Admin Guide
    3. Parameters
 
         3.1. db_url(str)
-        3.2. presentity_table(str)
-        3.3. active_watchers_table(str)
-        3.4. watchers_table(str)
-        3.5. clean_period (int)
-        3.6. db_update_period (int)
-        3.7. to_tag_pref (str)
-        3.8. expires_offset (int)
-        3.9. max_expires (int)
-        3.10. server_address (str)
-        3.11. subs_db_mode (int)
-        3.12. publ_cache (int)
-        3.13. subs_htable_size (int)
-        3.14. pres_htable_size (int)
-        3.15. send_fast_notify (int)
-        3.16. enable_sphere_check (int)
-        3.17. timeout_rm_subs (int)
-        3.18. fetch_rows (integer)
+        3.2. xcap_db_url(str)
+        3.3. presentity_table(str)
+        3.4. active_watchers_table(str)
+        3.5. watchers_table(str)
+        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)
 
    4. Functions
 
@@ -152,6 +163,7 @@ Chapter 1. Admin Guide
         4.3. pres_auth_status(watcher_uri, presentity_uri)
         4.4. pres_refresh_watchers(uri, event, type)
         4.5. pres_update_watchers(uri, event)
+        4.6. pres_update_presentity(uri, file_uri, filename)
 
    5. MI Commands
 
@@ -205,23 +217,26 @@ Chapter 1. Admin Guide
 3. Parameters
 
    3.1. db_url(str)
-   3.2. presentity_table(str)
-   3.3. active_watchers_table(str)
-   3.4. watchers_table(str)
-   3.5. clean_period (int)
-   3.6. db_update_period (int)
-   3.7. to_tag_pref (str)
-   3.8. expires_offset (int)
-   3.9. max_expires (int)
-   3.10. server_address (str)
-   3.11. subs_db_mode (int)
-   3.12. publ_cache (int)
-   3.13. subs_htable_size (int)
-   3.14. pres_htable_size (int)
-   3.15. send_fast_notify (int)
-   3.16. enable_sphere_check (int)
-   3.17. timeout_rm_subs (int)
-   3.18. fetch_rows (integer)
+   3.2. xcap_db_url(str)
+   3.3. presentity_table(str)
+   3.4. active_watchers_table(str)
+   3.5. watchers_table(str)
+   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.1. db_url(str)
 
@@ -238,41 +253,69 @@ modparam("presence", "db_url",
         "mysql://openser:openserrw@localhost/openser")
 ...
 
-3.2. presentity_table(str)
+3.2. xcap_db_url(str)
+
+   The XCAP database url.
+
+   This option is only required if integrated_xcap_server is enabled AND
+   the xcap table is on a difference server from the other presence
+   tables. If this option is not set db_url is used for the
+   integrated_xcap_server when required.
+
+   Default value is “NULL”.
+
+   Example 1.2. Set xcap_db_url parameter
+...
+modparam("presence", "xcap_db_url",
+        "mysql://openser:openserrw@localhost/openser")
+...
+
+3.3. presentity_table(str)
 
    The name of the db table where PUBLISH presence information is stored.
 
    Default value is “presentity”.
 
-   Example 1.2. Set presentity_table parameter
+   Example 1.3. Set presentity_table parameter
 ...
 modparam("presence", "presentity_table", "presentity")
 ...
 
-3.3. active_watchers_table(str)
+3.4. active_watchers_table(str)
 
    The name of the db table where active subscription information is
    stored.
 
    Default value is “active_watchers”.
 
-   Example 1.3. Set active_watchers_table parameter
+   Example 1.4. Set active_watchers_table parameter
 ...
 modparam("presence", "active_watchers_table", "active_watchers")
 ...
 
-3.4. watchers_table(str)
+3.5. watchers_table(str)
 
    The name of the db table where subscription states are stored.
 
    Default value is “watchers”.
 
-   Example 1.4. Set watchers_table parameter
+   Example 1.5. Set watchers_table parameter
 ...
 modparam("presence", "watchers_table", "watchers")
 ...
 
-3.5. clean_period (int)
+3.6. xcap_table(str)
+
+   The name of the db table where XML documents are stored.
+
+   Default value is “xcap”.
+
+   Example 1.6. Set xcap_table parameter
+...
+modparam("presence", "xcap_table", "xcap")
+...
+
+3.7. clean_period (int)
 
    The period in seconds between checks if there are expired messages
    stored in database.
@@ -280,12 +323,12 @@ modparam("presence", "watchers_table", "watchers")
    Default value is “100”. A zero or negative value disables this
    activity.
 
-   Example 1.5. Set clean_period parameter
+   Example 1.7. Set clean_period parameter
 ...
 modparam("presence", "clean_period", 100)
 ...
 
-3.6. db_update_period (int)
+3.8. db_update_period (int)
 
    The period at which to synchronize cached subscriber info with the
    database.
@@ -293,24 +336,24 @@ modparam("presence", "clean_period", 100)
    Default value is “100”. A zero or negative value disables
    synchronization.
 
-   Example 1.6. Set db_update_period parameter
+   Example 1.8. Set db_update_period parameter
 ...
 modparam("presence", "db_update_period", 100)
 ...
 
-3.7. to_tag_pref (str)
+3.9. to_tag_pref (str)
 
    The prefix used when generating to_tag when sending replies for
    SUBSCRIBE requests.
 
    Default value is “10”.
 
-   Example 1.7. Set to_tag_pref parameter
+   Example 1.9. Set to_tag_pref parameter
 ...
 modparam("presence", "to_tag_pref", 'pres')
 ...
 
-3.8. expires_offset (int)
+3.10. 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
@@ -318,35 +361,35 @@ modparam("presence", "to_tag_pref", 'pres')
 
    Default value is “0”.
 
-   Example 1.8. Set expires_offset parameter
+   Example 1.10. Set expires_offset parameter
 ...
 modparam("presence", "expires_offset", 10)
 ...
 
-3.9. max_expires (int)
+3.11. max_expires (int)
 
    The the maximum admissible expires value for PUBLISH/SUBSCRIBE message
    (in seconds).
 
    Default value is “3600”.
 
-   Example 1.9. Set max_expires parameter
+   Example 1.11. Set max_expires parameter
 ...
 modparam("presence", "max_expires", 3600)
 ...
 
-3.10. server_address (str)
+3.12. 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.10. Set server_address parameter
+   Example 1.12. Set server_address parameter
 ...
 modparam("presence", "server_address", "sip:10.10.10.10:5060")
 ...
 
-3.11. subs_db_mode (int)
+3.13. subs_db_mode (int)
 
    The presence module can utilize database for persistent subscription
    storage. If you use database, your subscriptions will survive machine
@@ -380,12 +423,12 @@ modparam("presence", "server_address", "sip:10.10.10.10:5060")
 
    Default value is 2 (Write-Back scheme).
 
-   Example 1.11. Set subs_db_mode parameter
+   Example 1.13. Set subs_db_mode parameter
 ...
 modparam("presence", "subs_db_mode", 1)
 ...
 
-3.12. publ_cache (int)
+3.14. 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
@@ -401,12 +444,12 @@ modparam("presence", "subs_db_mode", 1)
 
    Default value is “1”.
 
-   Example 1.12. Set publ_cache parameter
+   Example 1.14. Set publ_cache parameter
 ...
 modparam("presence", "publ_cache", 0)
 ...
 
-3.13. subs_htable_size (int)
+3.15. 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
@@ -414,24 +457,24 @@ modparam("presence", "publ_cache", 0)
 
    Default value is “9 (512)”.
 
-   Example 1.13. Set subs_htable_size parameter
+   Example 1.15. Set subs_htable_size parameter
 ...
 modparam("presence", "subs_htable_size", 11)
 ...
 
-3.14. pres_htable_size (int)
+3.16. 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.14. Set pres_htable_size parameter
+   Example 1.16. Set pres_htable_size parameter
 ...
 modparam("presence", "pres_htable_size", 11)
 ...
 
-3.15. send_fast_notify (int)
+3.17. 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
@@ -441,12 +484,12 @@ modparam("presence", "pres_htable_size", 11)
 
    Default value is “1 ”.
 
-   Example 1.15. Set send_fast_notify parameter
+   Example 1.17. Set send_fast_notify parameter
 ...
 modparam("presence", "send_fast_notify", 0)
 ...
 
-3.16. enable_sphere_check (int)
+3.18. 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
@@ -456,12 +499,12 @@ modparam("presence", "send_fast_notify", 0)
 
    Default value is “0 ”.
 
-   Example 1.16. Set enable_sphere_check parameter
+   Example 1.18. Set enable_sphere_check parameter
 ...
 modparam("presence", "enable_sphere_check", 1)
 ...
 
-3.17. timeout_rm_subs (int)
+3.19. 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
@@ -471,22 +514,36 @@ modparam("presence", "enable_sphere_check", 1)
 
    Default value is “1”.
 
-   Example 1.17. Set timeout_rm_subs parameter
+   Example 1.19. Set timeout_rm_subs parameter
 ...
 modparam("presence", "timeout_rm_subs", 0)
 ...
 
-3.18. fetch_rows (integer)
+3.20. fetch_rows (integer)
 
    Number of rows to be loaded in one step from database.
 
    Default value is 500.
 
-   Example 1.18. Set fetch_rows parameter
+   Example 1.20. Set fetch_rows parameter
 ...
 modparam("presence", "fetch_rows", 1000)
 ...
 
+3.21. 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
+   means no integrated XCAP server, setting to 1 means enable integrated
+   XCAP server.
+
+   Default value is 0.
+
+   Example 1.21. Set integrated_xcap_server parameter
+...
+modparam("presence", "integrated_xcap_server", 1)
+...
+
 4. Functions
 
    4.1. handle_publish(char* sender_uri)
@@ -494,6 +551,7 @@ modparam("presence", "fetch_rows", 1000)
    4.3. pres_auth_status(watcher_uri, presentity_uri)
    4.4. pres_refresh_watchers(uri, event, type)
    4.5. pres_update_watchers(uri, event)
+   4.6. pres_update_presentity(uri, file_uri, filename)
 
 4.1.  handle_publish(char* sender_uri)
 
@@ -515,7 +573,7 @@ modparam("presence", "fetch_rows", 1000)
 
    The module sends an appropriate stateless reply in all cases.
 
-   Example 1.19. handle_publish usage
+   Example 1.22. handle_publish usage
 ...
         if(is_method("PUBLISH"))
         {
@@ -541,7 +599,7 @@ modparam("presence", "fetch_rows", 1000)
 
    The module sends an appropriate stateless reply in all cases.
 
-   Example 1.20. handle_subscribe usage
+   Example 1.23. handle_subscribe usage
 ...
 if(method=="SUBSCRIBE")
     handle_subscribe();
@@ -558,7 +616,7 @@ if(method=="SUBSCRIBE")
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 1.21. pres_auth_status usage
+   Example 1.24. pres_auth_status usage
 ...
 if (method=="MESSAGE") {
     pres_auth_status("$fu", $ru");
@@ -589,7 +647,7 @@ if (method=="MESSAGE") {
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.22. pres_refresh_watchers usage
+   Example 1.25. pres_refresh_watchers usage
 ...
 pres_refresh_watchers("sip:[email protected]", "presence", 1);
 ...
@@ -607,11 +665,31 @@ pres_refresh_watchers("sip:[email protected]", "presence", 1);
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.23. pres_update_watchers usage
+   Example 1.26. pres_update_watchers usage
 ...
 pres_update_watchers("sip:[email protected]", "presence");
 ...
 
+4.6.  pres_update_presentity(uri, file_uri, filename)
+
+   The function can be used in configuration to triger updates to
+   presentities based on the content of pidf-manipulation documents stored
+   in the xcap db table. This enables "hard-state" presence to be set.
+
+   Parameters:
+     * uri - the uri of the presentity
+     * file_uri - the uri of the document on the XCAP server
+     * filename - the filename part of the uri of the document this is
+       used as the presentity ETag
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.27. pres_update_presentity usage
+...
+pres_update_presentity("$xcapuri(u=>xuid)", "$xcapuri(u=>uri_adoc)", "$xcapuri(u
+=>file)");
+...
+
 5. MI Commands
 
    5.1. refreshWatchers

+ 99 - 0
modules_k/presence/doc/presence_admin.xml

@@ -103,6 +103,31 @@ modparam("presence", "db_url",
 </programlisting>
 		</example>
 	</section>
+	<section>
+		<title><varname>xcap_db_url</varname>(str)</title>
+		<para>
+		The XCAP database url.
+		</para>
+		<para>This option is only required if integrated_xcap_server is
+		enabled AND the xcap table is on a difference server from the
+		other presence tables.  If this option is not set db_url is
+		used for the integrated_xcap_server when required.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>NULL</quote>.</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xcap_db_url</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("presence", "xcap_db_url", 
+	"&defaultdb;")
+...
+</programlisting>
+		</example>
+	</section>
+
+
 	<section>
 		<title><varname>presentity_table</varname>(str)</title>
 		<para>
@@ -154,6 +179,24 @@ modparam("presence", "active_watchers_table", "active_watchers")
 ...
 modparam("presence", "watchers_table", "watchers")
 ...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>xcap_table</varname>(str)</title>
+		<para>
+		The name of the db table where XML documents are stored.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>xcap</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xcap_table</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("presence", "xcap_table", "xcap")
+...
 </programlisting>
 		</example>
 	</section>
@@ -490,6 +533,28 @@ modparam("presence", "timeout_rm_subs", 0)
 ...
 modparam("presence", "fetch_rows", 1000)
 ...
+</programlisting>
+	    </example>
+	</section>
+	<section>
+	    <title><varname>integrated_xcap_server</varname> (integer)</title>
+	    <para>
+		Enable the integrated XCAP server connection.  This is only
+		required if you want to use the pres_update_presentity()
+		function.  Setting to 0 means no integrated XCAP server,
+		setting to 1 means enable integrated XCAP server.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is 0.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>integrated_xcap_server</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("presence", "integrated_xcap_server", 1)
+...
 </programlisting>
 	    </example>
 	</section>
@@ -710,6 +775,40 @@ pres_refresh_watchers("sip:[email protected]", "presence", 1);
 ...
 pres_update_watchers("sip:[email protected]", "presence");
 ...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title>
+		<function moreinfo="none">pres_update_presentity(uri, file_uri, filename)</function>
+		</title>
+		<para>
+			The function can be used in configuration to triger updates to presentities
+			based on the content of pidf-manipulation documents stored in the xcap db
+			table.  This enables "hard-state" presence to be set.
+		</para>
+		<para>Parameters:</para>
+		<itemizedlist>
+			<listitem>
+				<para>uri - the uri of the presentity</para>
+			</listitem>
+			<listitem>
+				<para>file_uri - the uri of the document on the XCAP server</para>
+			</listitem>
+			<listitem>
+				<para>filename - the filename part of the uri of the document
+					this is used as the presentity ETag</para>
+			</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>pres_update_presentity</function> usage</title>
+		<programlisting format="linespecific">
+...
+pres_update_presentity("$xcapuri(u=>xuid)", "$xcapuri(u=>uri_adoc)", "$xcapuri(u=>file)");
+...
 </programlisting>
 		</example>
 	</section>

+ 125 - 3
modules_k/presence/presence.c

@@ -78,6 +78,7 @@ MODULE_VERSION
 #define S_TABLE_VERSION  3
 #define P_TABLE_VERSION  3
 #define ACTWATCH_TABLE_VERSION 9
+#define XCAP_TABLE_VERSION 4
 
 char *log_buf = NULL;
 static int clean_period=100;
@@ -86,9 +87,12 @@ static int db_update_period=100;
 /* database connection */
 db1_con_t *pa_db = NULL;
 db_func_t pa_dbf;
+db1_con_t *pres_xcap_db = NULL;
+db_func_t pres_xcap_dbf;
 str presentity_table= str_init("presentity");
 str active_watchers_table = str_init("active_watchers");
 str watchers_table= str_init("watchers");
+str pres_xcap_table= str_init("xcap");
 
 int pres_fetch_rows = 500;
 int library_mode= 0;
@@ -124,12 +128,14 @@ static int w_pres_update_watchers(struct sip_msg *msg, char *puri,
 		char *pevent);
 static int fixup_refresh_watchers(void** param, int param_no);
 static int fixup_update_watchers(void** param, int param_no);
+static int fixup_update_presentity(void** param, int param_no);
 
 int counter =0;
 int pid = 0;
 char prefix='a';
 int startup_time=0;
 str db_url = {0, 0};
+str pres_xcap_db_url = {0, 0};
 int expires_offset = 0;
 int max_expires= 3600;
 int shtable_size= 9;
@@ -139,6 +145,7 @@ int sphere_enable= 0;
 int timeout_rm_subs = 1;
 int send_fast_notify = 1;
 int publ_cache_enabled = 1;
+int pres_integrated_xcap_server = 0;
 
 int phtable_size= 9;
 phtable_t* pres_htable=NULL;
@@ -157,6 +164,8 @@ static cmd_export_t cmds[]=
 		fixup_refresh_watchers, 0, ANY_ROUTE},
 	{"pres_update_watchers",  (cmd_function)w_pres_update_watchers,  2,
 		fixup_update_watchers, 0, ANY_ROUTE},
+	{"pres_update_presentity", (cmd_function)pres_update_presentity, 3,
+		fixup_update_presentity, 0, ANY_ROUTE},
 	{"bind_presence",         (cmd_function)bind_presence,           1,
 		0, 0, 0},
 	{ 0, 0, 0, 0, 0, 0}
@@ -164,9 +173,11 @@ static cmd_export_t cmds[]=
 
 static param_export_t params[]={
 	{ "db_url",                 STR_PARAM, &db_url.s},
+	{ "xcap_db_url",            STR_PARAM, &pres_xcap_db_url.s},
 	{ "presentity_table",       STR_PARAM, &presentity_table.s},
 	{ "active_watchers_table",  STR_PARAM, &active_watchers_table.s},
 	{ "watchers_table",         STR_PARAM, &watchers_table.s},
+	{ "xcap_table",             STR_PARAM, &pres_xcap_table.s},
 	{ "clean_period",           INT_PARAM, &clean_period },
 	{ "db_update_period",       INT_PARAM, &db_update_period },
 	{ "to_tag_pref",            STR_PARAM, &to_tag_pref },
@@ -181,6 +192,7 @@ static param_export_t params[]={
 	{ "timeout_rm_subs",        INT_PARAM, &timeout_rm_subs},
 	{ "send_fast_notify",       INT_PARAM, &send_fast_notify},
 	{ "fetch_rows",             INT_PARAM, &pres_fetch_rows},
+	{ "integrated_xcap_server", INT_PARAM, &pres_integrated_xcap_server},
     {0,0,0}
 };
 
@@ -223,8 +235,21 @@ static int mod_init(void)
 	active_watchers_table.len = strlen(active_watchers_table.s);
 	watchers_table.len = strlen(watchers_table.s);
 
+	if(pres_integrated_xcap_server == 1)
+	{
+		pres_xcap_db_url.s = pres_xcap_db_url.s ? pres_xcap_db_url.s : db_url.s;
+		pres_xcap_db_url.len = strlen(pres_xcap_db_url.s);
+		pres_xcap_table.len = strlen(pres_xcap_table.s);
+	}
+
 	if(db_url.s== NULL)
 		library_mode= 1;
+	else if(pres_integrated_xcap_server == 1)
+	{
+		pres_xcap_db_url.s = pres_xcap_db_url.s ? pres_xcap_db_url.s : db_url.s;
+		pres_xcap_db_url.len = strlen(pres_xcap_db_url.s);
+		pres_xcap_table.len = strlen(pres_xcap_table.s);
+	}
 
 	EvList= init_evlist();
 	if(!EvList){
@@ -280,7 +305,6 @@ static int mod_init(void)
 		LM_ERR("Database module not found\n");
 		return -1;
 	}
-	
 
 	if (!DB_CAPABILITY(pa_dbf, DB_CAP_ALL))
 	{
@@ -295,20 +319,52 @@ static int mod_init(void)
 		LM_ERR("Connection to database failed\n");
 		return -1;
 	}
-	
 	/*verify table versions */
 	if((db_check_table_version(&pa_dbf, pa_db, &presentity_table, P_TABLE_VERSION) < 0) ||
 		(db_check_table_version(&pa_dbf, pa_db, &watchers_table, S_TABLE_VERSION) < 0)) {
 			LM_ERR("error during table version check\n");
 			return -1;
 	}
-
 	if(subs_dbmode != NO_DB &&
 		db_check_table_version(&pa_dbf, pa_db, &active_watchers_table, ACTWATCH_TABLE_VERSION) < 0) {
 		LM_ERR("wrong table version for %s\n", active_watchers_table.s);
 		return -1;
 	}
 
+	if (pres_integrated_xcap_server == 1)
+	{
+		if(pres_xcap_db_url.s== NULL)
+		{
+			LM_ERR("xcap database url not set!\n");
+			return -1;
+		}
+
+		if (db_bind_mod(&pres_xcap_db_url, &pres_xcap_dbf))
+		{
+			LM_ERR("Database module not found\n");
+			return -1;
+		}
+
+		if (!DB_CAPABILITY(pres_xcap_dbf, DB_CAP_ALL))
+		{
+			LM_ERR("Database module does not implement all functions"
+					" needed by presence module\n");
+			return -1;
+		}
+
+		pres_xcap_db = pres_xcap_dbf.init(&pres_xcap_db_url);
+		if (!pres_xcap_db)
+		{
+			LM_ERR("Connection to database failed\n");
+			return -1;
+		}
+
+		if(db_check_table_version(&pres_xcap_dbf, pres_xcap_db, &pres_xcap_table, XCAP_TABLE_VERSION) < 0) {
+				LM_ERR("error during table version check\n");
+				return -1;
+		}
+	}
+
 	if(subs_dbmode != DB_ONLY) {
 		if(shtable_size< 1)
 			shtable_size= 512;
@@ -361,6 +417,12 @@ static int mod_init(void)
 	pa_dbf.close(pa_db);
 	pa_db = NULL;
 
+	if (pres_integrated_xcap_server == 1)
+	{
+		pres_xcap_dbf.close(pres_xcap_db);
+		pres_xcap_db = NULL;
+	}
+
 	return 0;
 }
 
@@ -410,6 +472,29 @@ static int child_init(int rank)
 		return -1;
 	}
 
+	if (pres_integrated_xcap_server == 1)
+	{
+		if (pres_xcap_dbf.init==0)
+		{
+			LM_CRIT("child_init: database not bound\n");
+			return -1;
+		}
+		if (pres_xcap_db)
+			return 0;
+		pres_xcap_db = pres_xcap_dbf.init(&pres_xcap_db_url);
+		if (!pres_xcap_db)
+		{
+			LM_ERR("child %d: unsuccessful connecting to database\n", rank);
+			return -1;
+		}
+	
+		if (pres_xcap_dbf.use_table(pres_xcap_db, &pres_xcap_table) < 0)
+		{
+			LM_ERR( "child %d:unsuccessful use_table xcap_table\n", rank);
+			return -1;
+		}
+	}
+
 	LM_DBG("child %d: Database connection opened successfully\n", rank);
 	
 	return 0;
@@ -452,6 +537,29 @@ static int mi_child_init(void)
 		return -1;
 	}
 
+	if (pres_integrated_xcap_server == 1)
+	{
+		if (pres_xcap_dbf.init==0)
+		{
+			LM_CRIT("database not bound\n");
+			return -1;
+		}
+		if (pres_xcap_db)
+			return 0;
+		pres_xcap_db = pres_xcap_dbf.init(&db_url);
+		if (!pres_xcap_db)
+		{
+			LM_ERR("connecting database\n");
+			return -1;
+		}
+	
+		if (pres_xcap_dbf.use_table(pres_xcap_db, &pres_xcap_table) < 0)
+		{
+			LM_ERR( "unsuccessful use_table xcap_table\n");
+			return -1;
+		}
+	}
+
 	LM_DBG("Database connection opened successfully\n");
 	return 0;
 }
@@ -480,6 +588,9 @@ static void destroy(void)
 	if(pa_db && pa_dbf.close)
 		pa_dbf.close(pa_db);
 
+	if(pres_xcap_db && pres_xcap_dbf.close)
+		pres_xcap_dbf.close(pres_xcap_db);
+
 	destroy_evlist();
 }
 
@@ -1593,3 +1704,14 @@ static int fixup_update_watchers(void** param, int param_no)
 	}
 	return 0;
 }
+
+/**
+ * fixup for pres_update_presentity
+ */
+static int fixup_update_presentity(void** param, int param_no)
+{
+	if(param_no==1 || param_no==2 || param_no==3)
+		return fixup_spve_null(param, 1);
+
+	return 0;
+}

+ 5 - 0
modules_k/presence/presence.h

@@ -60,6 +60,8 @@ extern sl_api_t slb;
 /* DB module bind */
 extern db_func_t pa_dbf;
 extern db1_con_t* pa_db;
+extern db_func_t pres_xcap_dbf;
+extern db1_con_t* pres_xcap_db;
 
 /* PRESENCE database */
 extern str db_url;
@@ -85,6 +87,9 @@ extern shtable_t subs_htable;
 extern int phtable_size;
 extern phtable_t* pres_htable;
 
+extern int pres_integrated_xcap_server;
+extern str pres_xcap_table;
+
 int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc);
 int pres_auth_status(struct sip_msg* msg, str watcher_uri, str presentity_uri);
 

+ 62 - 25
modules_k/presence/presentity.c

@@ -98,6 +98,9 @@ int publ_send200ok(struct sip_msg *msg, int lexpire, str etag)
 	int buf_len= 128, size;
 	str hdr_append= {0, 0}, hdr_append2= {0, 0} ;
 
+	if (msg == NULL)
+		return 0;
+
 	LM_DBG("send 200OK reply\n");	
 	LM_DBG("etag= %s - len= %d\n", etag.s, etag.len);
 	
@@ -288,7 +291,7 @@ int update_presentity(struct sip_msg* msg, presentity_t* presentity, str* body,
 	int ret = -1;
 	int db_record_exists = 0;
 
-	*sent_reply= 0;
+	if (sent_reply) *sent_reply= 0;
 	if(presentity->event->req_auth)
 	{
 		/* get rules_document */
@@ -350,13 +353,6 @@ int update_presentity(struct sip_msg* msg, presentity_t* presentity, str* body,
 		}
 		
 		/* insert new record into database */	
-		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 = presentity->expires+
-				(int)time(NULL);
-		n_query_cols++;
-	
 		query_cols[n_query_cols] = &str_sender_col;
 		query_vals[n_query_cols].type = DB1_STR;
 		query_vals[n_query_cols].nul = 0;
@@ -382,25 +378,63 @@ int update_presentity(struct sip_msg* msg, presentity_t* presentity, str* body,
 		query_vals[n_query_cols].val.int_val = presentity->received_time;
 		n_query_cols++;
 		
-		if (pa_dbf.use_table(pa_db, &presentity_table) < 0) 
+		if (presentity->expires != -1)
 		{
-			LM_ERR("unsuccessful use_table\n");
-			goto error;
-		}
+			/* A real PUBLISH */
+			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 = presentity->expires+
+								(int)time(NULL);
+			n_query_cols++;
+	
+			if (pa_dbf.use_table(pa_db, &presentity_table) < 0) 
+			{
+				LM_ERR("unsuccessful use_table\n");
+				goto error;
+			}
 
-		LM_DBG("inserting %d cols into table\n",n_query_cols);
+			LM_DBG("inserting %d cols into table\n",n_query_cols);
 				
-		if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0) 
+			if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0) 
+			{
+				LM_ERR("inserting new record in database\n");
+				goto error;
+			}
+		}
+		else
 		{
-			LM_ERR("inserting new record in database\n");
-			goto error;
+			/* A hard-state PUBLISH */
+			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 = -1;
+			n_query_cols++;
+	
+			if (pa_dbf.use_table(pa_db, &presentity_table) < 0) 
+			{
+				LM_ERR("unsuccessful use_table\n");
+				goto error;
+			}
+
+			if (pa_dbf.replace == NULL)
+			{
+				LM_ERR("replace is required for pidf-manipulation support\n");
+				goto error;
+			}
+			if (pa_dbf.replace(pa_db, query_cols, query_vals, n_query_cols, 4, 0) < 0) 
+			{
+				LM_ERR("replacing record in database\n");
+				goto error;
+			}
 		}
+
 		if( publ_send200ok(msg, presentity->expires, presentity->etag)< 0)
 		{
 			LM_ERR("sending 200OK\n");
 			goto error;
 		}
-		*sent_reply= 1;
+		if (sent_reply) *sent_reply= 1;
 		goto send_notify;
 	}
 	else
@@ -468,7 +502,7 @@ after_dialog_check:
 			
 		}
 
-		if(presentity->expires == 0) 
+		if(presentity->expires <= 0) 
 		{
 
 			if (!db_record_exists)
@@ -496,7 +530,7 @@ after_dialog_check:
 				LM_ERR("sending 200OK reply\n");
 				goto error;
 			}
-			*sent_reply= 1;
+			if (sent_reply) *sent_reply= 1;
 			if( publ_notify( presentity, pres_uri, body, &presentity->etag, rules_doc)< 0 )
 			{
 				LM_ERR("while sending notify\n");
@@ -541,7 +575,7 @@ after_dialog_check:
 					LM_ERR("sending 200OK reply\n");
 					goto error;
 				}
-			*sent_reply= 1;
+			if (sent_reply) *sent_reply= 1;
 			goto done;
 		}
 
@@ -683,7 +717,7 @@ after_dialog_check:
 			LM_ERR("sending 200OK reply\n");
 			goto error;
 		}
-		*sent_reply= 1;
+		if (sent_reply) *sent_reply= 1;
 
 		if(etag.s)
 			pkg_free(etag.s);
@@ -717,12 +751,15 @@ done:
 send_412:
 
 	LM_ERR("No E_Tag match %*s\n", presentity->etag.len, presentity->etag.s);
-	if (slb.freply(msg, 412, &pu_412_rpl) < 0)
+	if (msg != NULL)
 	{
-		LM_ERR("sending '412 Conditional request failed' reply\n");
-		goto error;
+		if (slb.freply(msg, 412, &pu_412_rpl) < 0)
+		{
+			LM_ERR("sending '412 Conditional request failed' reply\n");
+			goto error;
+		}
 	}
-	*sent_reply= 1;
+	if (sent_reply) *sent_reply= 1;
 	ret = 0;
 
 error:

+ 197 - 3
modules_k/presence/publish.c

@@ -33,6 +33,7 @@
 
 #include "../../ut.h"
 #include "../../str.h"
+#include "../../mod_fix.h"
 #include "../../parser/parse_to.h"
 #include "../../parser/parse_uri.h" 
 #include "../../parser/parse_expires.h" 
@@ -47,6 +48,7 @@
 #include "utils_func.h"
 #include "publish.h"
 #include "presentity.h"
+#include "../xcap_client/xcap_callbacks.h"
 
 extern gen_lock_set_t* set;
 
@@ -55,6 +57,10 @@ static str pu_400b_rpl = str_init("Invalid request");
 static str pu_500_rpl  = str_init("Server Internal Error");
 static str pu_489_rpl  = str_init("Bad Event");
 
+static str str_doc_uri_col = str_init("doc_uri");
+static str str_doc_type_col = str_init("doc_type");
+static str str_doc_col = str_init("doc");
+
 struct p_modif
 {
 	presentity_t* p;
@@ -66,7 +72,7 @@ void msg_presentity_clean(unsigned int ticks,void *param)
 	db_key_t db_keys[2];
 	db_val_t db_vals[2];
 	db_op_t  db_ops[2] ;
-	db_key_t result_cols[6];
+	db_key_t result_cols[4];
 	db1_res_t *result = NULL;
 	db_row_t *row ;
 	db_val_t *row_vals ;
@@ -94,6 +100,12 @@ void msg_presentity_clean(unsigned int ticks,void *param)
 	db_vals[0].nul = 0;
 	db_vals[0].val.int_val = (int)time(NULL);
 
+	db_keys[1] = &str_expires_col;
+	db_ops[1] = OP_GT;
+	db_vals[1].type = DB1_INT;
+	db_vals[1].nul = 0;
+	db_vals[1].val.int_val = 0;
+
 	result_cols[user_col= n_result_cols++] = &str_username_col;
 	result_cols[domain_col=n_result_cols++] = &str_domain_col;
 	result_cols[etag_col=n_result_cols++] = &str_etag_col;
@@ -101,7 +113,7 @@ void msg_presentity_clean(unsigned int ticks,void *param)
 
 	static str query_str = str_init("username");
 	if(pa_dbf.query(pa_db, db_keys, db_ops, db_vals, result_cols,
-						1, n_result_cols, &query_str, &result )< 0)
+						2, n_result_cols, &query_str, &result )< 0)
 	{
 		LM_ERR("failed to query database for expired messages\n");
 		if(result)
@@ -235,7 +247,7 @@ void msg_presentity_clean(unsigned int ticks,void *param)
 	}
 
 delete_pres:
-	if (pa_dbf.delete(pa_db, db_keys, db_ops, db_vals, 1) < 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;
@@ -531,4 +543,186 @@ error:
 
 }
 
+static int fetch_presentity(str furi, str *presentity)
+{
+	db_key_t query_cols[2], result_cols[1];
+	db_val_t query_vals[2], *row_vals;
+	db1_res_t *result;
+	db_row_t *row;
+	int n_query_cols = 0, n_result_cols = 0;;
+	char *tmp;
+
+	query_cols[n_query_cols] = &str_doc_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 = furi;
+	n_query_cols++;
+
+	query_cols[n_query_cols] = &str_doc_type_col;
+	query_vals[n_query_cols].type = DB1_INT;
+	query_vals[n_query_cols].nul = 0;
+	query_vals[n_query_cols].val.int_val = PIDF_MANIPULATION;
+	n_query_cols++;
+
+	result_cols[n_result_cols++] = &str_doc_col;
+
+	if (pres_xcap_dbf.use_table(pres_xcap_db, &pres_xcap_table) < 0)
+	{
+		LM_ERR("calling use_table()\n");
+		return -1;
+	}
+
+	if (pres_xcap_dbf.query(pres_xcap_db, query_cols, 0, query_vals, result_cols,
+				n_query_cols, n_result_cols, 0, &result) < 0)
+	{
+		LM_ERR("calling query()\n");
+		return -1;
+	}
+
+	if (result->n <=0)
+	{
+		pres_xcap_dbf.free_result(pres_xcap_db, result);
+		return 0;
+	}
+
+	if (result->n > 1)
+	{
+		pres_xcap_dbf.free_result(pres_xcap_db, result);
+		return -1;
+	}
+
+	row = &result->rows[0];
+	row_vals = ROW_VALUES(row);
+
+	tmp = (char *)row_vals[0].val.string_val;
+	if (tmp == NULL)
+	{
+		LM_ERR("xcap document is empty\n");
+		pres_xcap_dbf.free_result(pres_xcap_db, result);
+		return -1;
+	}
+	presentity->len = strlen(tmp);
+
+	presentity->s = pkg_malloc(presentity->len * sizeof(char));
+	if (presentity->s == NULL)
+	{
+		LM_ERR("allocating memory\n");
+		pres_xcap_dbf.free_result(pres_xcap_db, result);
+		return -1;
+	}
+	memcpy(presentity->s, tmp, presentity->len);
+
+	pres_xcap_dbf.free_result(pres_xcap_db, result);
+	return 1;
+}
+
+int pres_update_presentity(struct sip_msg *msg, char *puri, char *furi, char *fname)
+{
+	int pres_result, ret = -1, new_t;
+	char *sphere = NULL;
+	str pres_uri, file_uri, filename, presentity, ev;
+	pres_ev_t *event;
+	presentity_t *pres = NULL;
+	struct sip_uri parsed_uri;
+
+	if(fixup_get_svalue(msg, (gparam_p)puri, &pres_uri)!=0)
+	{
+		LM_ERR("invalid uri parameter");
+		return -1;
+	}
+	if(fixup_get_svalue(msg, (gparam_p)furi, &file_uri)!=0)
+	{
+		LM_ERR("invalid file_uri parameter");
+		return -1;
+	}
+	if(fixup_get_svalue(msg, (gparam_p)fname, &filename)!=0)
+	{
+		LM_ERR("invalid filename parameter");
+		return -1;
+	}
+
+	LM_INFO("Hard-state file %.*s (uri %.*s) updated for %.*s\n",
+		filename.len, filename.s,
+		file_uri.len, file_uri.s,
+		pres_uri.len, pres_uri.s);
 
+	if (pres_integrated_xcap_server != 1)
+	{
+		LM_ERR("integrated XCAP server not configured\n");
+		return -1;
+	}
+
+	ev.s = "presence";
+	ev.len = 8;
+	event = contains_event(&ev, NULL);
+	if (event == NULL)
+	{
+		LM_ERR("presence event not supported\n");
+		return -1;
+	}
+
+	if (parse_uri(pres_uri.s, pres_uri.len, &parsed_uri) < 0)
+	{
+		LM_ERR("bad presentity URI\n");
+		return -1;
+	}
+
+	pres_result = fetch_presentity(file_uri, &presentity);
+	if (pres_result < 0)
+	{
+		LM_ERR("retrieving presentity\n");
+		return -1;
+	}
+	else if (pres_result > 0)
+	{
+		/* Insert/replace presentity... */
+		LM_DBG("INSERT/REPLACE\n");
+		xmlDocPtr doc;
+
+		if (sphere_enable)
+			sphere = extract_sphere(presentity);
+
+		doc = xmlParseMemory(presentity.s, presentity.len);
+		if (doc == NULL)
+		{
+			LM_ERR("bad body format\n");
+			xmlFreeDoc(doc);
+			xmlCleanupParser();
+			xmlMemoryDump();
+			goto done;
+		}
+		xmlFreeDoc(doc);
+		xmlCleanupParser();
+		xmlMemoryDump();
+
+		new_t = 1;
+	}
+	else
+	{
+		/* Delete presentity... */
+		LM_DBG("DELETE\n");
+		new_t = 0;
+	}
+
+	pres = new_presentity(&parsed_uri.host, &parsed_uri.user, -1, event, &filename, NULL);
+	if (pres == NULL)
+	{
+		LM_ERR("creating presentity structure\n");
+		goto done;
+	}
+
+	if (update_presentity(NULL, pres, &presentity, new_t, NULL, sphere) < 0)
+	{
+		LM_ERR("updating presentity\n");
+		goto done;
+	}
+
+	ret = 1;
+
+done:
+	if (pres) pkg_free(pres);
+	if (sphere) pkg_free(sphere);
+	if (presentity.s) pkg_free(presentity.s);
+
+	return ret;
+}

+ 1 - 1
modules_k/presence/publish.h

@@ -48,5 +48,5 @@
 void msg_presentity_clean(unsigned int ticks,void *param);
 
 int handle_publish(struct sip_msg* msg, char* str1 ,char* str2);
-
+int pres_update_presentity(struct sip_msg *msg, char *puri, char *furi, char *fname);
 #endif