Browse Source

Merge pull request #486 from kamailio/lazedo/presence_improvements

presence: improvements for 4.4
Daniel-Constantin Mierla 9 years ago
parent
commit
2b9df7c32f

+ 17 - 1
lib/srdb1/schema/pr_active_watchers.xml

@@ -9,7 +9,7 @@
 
 
 <table id="active_watchers" xmlns:db="http://docbook.org/ns/docbook">
 <table id="active_watchers" xmlns:db="http://docbook.org/ns/docbook">
     <name>active_watchers</name>
     <name>active_watchers</name>
-    <version>11</version>
+    <version>12</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
     <description>
         <db:para>Table for the presence module. More information can be found at: &KAMAILIO_MOD_DOC;presence.html
         <db:para>Table for the presence module. More information can be found at: &KAMAILIO_MOD_DOC;presence.html
@@ -200,6 +200,22 @@
         <description>Update winfo flag</description>
         <description>Update winfo flag</description>
     </column>
     </column>
 
 
+    <column>
+        <name>flags</name>
+        <type>int</type>
+        <size>&flag_len;</size>
+        <default>0</default>
+        <description>Branch and contact flags</description>
+    </column>
+
+    <column>
+        <name>user_agent</name>
+        <type>string</type>
+        <size>&hf_len;</size>
+        <default/>
+        <description>User-Agent header field contains information about the UAC originating the request.</description>
+    </column>
+
     <index>
     <index>
         <name>active_watchers_idx</name>
         <name>active_watchers_idx</name>
         <colref linkend="callid"/>
         <colref linkend="callid"/>

+ 189 - 70
modules/presence/README

@@ -16,9 +16,9 @@ Edited by
 
 
 Juha Heinanen
 Juha Heinanen
 
 
-   Copyright © 2006 Voice Sistem SRL
+   Copyright © 2006 Voice Sistem SRL
 
 
-   Copyright © 2009 Juha Heinanen
+   Copyright © 2009 Juha Heinanen
      __________________________________________________________________
      __________________________________________________________________
 
 
    Table of Contents
    Table of Contents
@@ -61,6 +61,7 @@ Juha Heinanen
               3.26. subs_remove_match (int)
               3.26. subs_remove_match (int)
               3.27. xavp_cfg (str)
               3.27. xavp_cfg (str)
               3.28. retrieve_order (int)
               3.28. retrieve_order (int)
+              3.29. sip_uri_match (int)
 
 
         4. Functions
         4. Functions
 
 
@@ -81,7 +82,16 @@ Juha Heinanen
 
 
               6.1. presence.cleanup
               6.1. presence.cleanup
 
 
-        7. Installation
+        7. Pseudo Variables
+
+              7.1. $subs(attr)
+              7.2. $notify_reply(attr)
+
+        8. Events
+
+              8.1. present:notify-reply
+
+        9. Installation
 
 
    2. Developer Guide
    2. Developer Guide
 
 
@@ -133,11 +143,15 @@ Juha Heinanen
    1.26. Set subs_remove_match parameter
    1.26. Set subs_remove_match parameter
    1.27. Set xavp_cfg parameter
    1.27. Set xavp_cfg parameter
    1.28. Set retrieve_order parameter
    1.28. Set retrieve_order parameter
-   1.29. handle_publish usage
-   1.30. handle_subscribe usage
-   1.31. pres_auth_status usage
-   1.32. pres_refresh_watchers usage
-   1.33. pres_update_watchers usage
+   1.29. Set sip_uri_match parameter
+   1.30. handle_publish usage
+   1.31. handle_subscribe usage
+   1.32. pres_auth_status usage
+   1.33. pres_refresh_watchers usage
+   1.34. pres_update_watchers usage
+   1.35. $subs(name) usage
+   1.36. $notify_reply(name) usage
+   1.37. $notify_reply(name) usage
    2.1. presence_api_t structure
    2.1. presence_api_t structure
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
@@ -180,6 +194,7 @@ Chapter 1. Admin Guide
         3.26. subs_remove_match (int)
         3.26. subs_remove_match (int)
         3.27. xavp_cfg (str)
         3.27. xavp_cfg (str)
         3.28. retrieve_order (int)
         3.28. retrieve_order (int)
+        3.29. sip_uri_match (int)
 
 
    4. Functions
    4. Functions
 
 
@@ -199,7 +214,16 @@ Chapter 1. Admin Guide
 
 
         6.1. presence.cleanup
         6.1. presence.cleanup
 
 
-   7. Installation
+   7. Pseudo Variables
+
+        7.1. $subs(attr)
+        7.2. $notify_reply(attr)
+
+   8. Events
+
+        8.1. present:notify-reply
+
+   9. Installation
 
 
 1. Overview
 1. Overview
 
 
@@ -273,6 +297,7 @@ Chapter 1. Admin Guide
    3.26. subs_remove_match (int)
    3.26. subs_remove_match (int)
    3.27. xavp_cfg (str)
    3.27. xavp_cfg (str)
    3.28. retrieve_order (int)
    3.28. retrieve_order (int)
+   3.29. sip_uri_match (int)
 
 
 3.1. db_url(str)
 3.1. db_url(str)
 
 
@@ -281,7 +306,7 @@ Chapter 1. Admin Guide
    If set, the module is a fully operational presence server. Otherwise,
    If set, the module is a fully operational presence server. Otherwise,
    it is used as a 'library', for its exported functions.
    it is used as a 'library', for its exported functions.
 
 
-   Default value is "NULL".
+   Default value is “NULL�.
 
 
    Example 1.1. Set db_url parameter
    Example 1.1. Set db_url parameter
 ...
 ...
@@ -293,7 +318,7 @@ modparam("presence", "db_url",
 
 
    The name of the db table where PUBLISH presence information is stored.
    The name of the db table where PUBLISH presence information is stored.
 
 
-   Default value is "presentity".
+   Default value is “presentity�.
 
 
    Example 1.2. Set presentity_table parameter
    Example 1.2. Set presentity_table parameter
 ...
 ...
@@ -305,7 +330,7 @@ modparam("presence", "presentity_table", "presentity")
    The name of the db table where active subscription information is
    The name of the db table where active subscription information is
    stored.
    stored.
 
 
-   Default value is "active_watchers".
+   Default value is “active_watchers�.
 
 
    Example 1.3. Set active_watchers_table parameter
    Example 1.3. Set active_watchers_table parameter
 ...
 ...
@@ -316,7 +341,7 @@ modparam("presence", "active_watchers_table", "active_watchers")
 
 
    The name of the db table where subscription states are stored.
    The name of the db table where subscription states are stored.
 
 
-   Default value is "watchers".
+   Default value is “watchers�.
 
 
    Example 1.4. Set watchers_table parameter
    Example 1.4. Set watchers_table parameter
 ...
 ...
@@ -328,7 +353,7 @@ modparam("presence", "watchers_table", "watchers")
    The period in seconds between checks if there are expired messages
    The period in seconds between checks if there are expired messages
    stored in database.
    stored in database.
 
 
-   Default value is "100". A zero or negative value disables this
+   Default value is “100�. A zero or negative value disables this
    activity.
    activity.
 
 
    Example 1.5. Set clean_period parameter
    Example 1.5. Set clean_period parameter
@@ -341,7 +366,7 @@ modparam("presence", "clean_period", 100)
    The period at which to synchronize cached subscriber info with the
    The period at which to synchronize cached subscriber info with the
    database.
    database.
 
 
-   Default value is "100". A zero or negative value disables
+   Default value is “100�. A zero or negative value disables
    synchronization.
    synchronization.
 
 
    Example 1.6. Set db_update_period parameter
    Example 1.6. Set db_update_period parameter
@@ -359,7 +384,7 @@ modparam("presence", "db_update_period", 100)
    than 0. When notifier_processes is less than or equal to 0 NOTIFY
    than 0. When notifier_processes is less than or equal to 0 NOTIFY
    requests are sent immediately.
    requests are sent immediately.
 
 
-   Default value is "5".
+   Default value is “5�.
 
 
    Example 1.7. Set waitn_time parameter
    Example 1.7. Set waitn_time parameter
 ...
 ...
@@ -376,7 +401,7 @@ modparam("presence", "waitn_time", 10)
    Separate notifier processes are only run when subs_db_mode is 3 (DB
    Separate notifier processes are only run when subs_db_mode is 3 (DB
    only mode).
    only mode).
 
 
-   Default value is "10".
+   Default value is “10�.
 
 
    Example 1.8. Set notifier_poll_rate parameter
    Example 1.8. Set notifier_poll_rate parameter
 ...
 ...
@@ -396,7 +421,7 @@ modparam("presence", "notifier_poll_rate", 20)
    NOTIFY requests can be sent on a dialog at the same time, there are
    NOTIFY requests can be sent on a dialog at the same time, there are
    race conditions which result in CSeq re-use.
    race conditions which result in CSeq re-use.
 
 
-   Default value is "1".
+   Default value is “1�.
 
 
    Example 1.9. Set notifier_processes parameter
    Example 1.9. Set notifier_processes parameter
 ...
 ...
@@ -408,7 +433,7 @@ modparam("presence", "notifier_processes", 2)
    The prefix used when generating to_tag when sending replies for
    The prefix used when generating to_tag when sending replies for
    SUBSCRIBE requests.
    SUBSCRIBE requests.
 
 
-   Default value is "10".
+   Default value is “10�.
 
 
    Example 1.10. Set to_tag_pref parameter
    Example 1.10. Set to_tag_pref parameter
 ...
 ...
@@ -421,7 +446,7 @@ modparam("presence", "to_tag_pref", 'pres')
    when sending a 200OK for a publish. It is used for forcing the client
    when sending a 200OK for a publish. It is used for forcing the client
    to send an update before the old publish expires.
    to send an update before the old publish expires.
 
 
-   Default value is "0".
+   Default value is “0�.
 
 
    Example 1.11. Set expires_offset parameter
    Example 1.11. Set expires_offset parameter
 ...
 ...
@@ -433,7 +458,7 @@ modparam("presence", "expires_offset", 10)
    The maximum admissible expires value for PUBLISH/SUBSCRIBE message (in
    The maximum admissible expires value for PUBLISH/SUBSCRIBE message (in
    seconds).
    seconds).
 
 
-   Default value is "3600".
+   Default value is “3600�.
 
 
    Example 1.12. Set max_expires parameter
    Example 1.12. Set max_expires parameter
 ...
 ...
@@ -447,7 +472,7 @@ modparam("presence", "max_expires", 3600)
 
 
    If > 0 then min_expires_action parameter determines the response.
    If > 0 then min_expires_action parameter determines the response.
 
 
-   Default value is "0".
+   Default value is “0�.
 
 
    Example 1.13. Set min_expires parameter
    Example 1.13. Set min_expires parameter
             ...
             ...
@@ -459,12 +484,12 @@ modparam("presence", "max_expires", 3600)
    The action to take when UA sends a expires value less then min_expires.
    The action to take when UA sends a expires value less then min_expires.
 
 
    Possible Values
    Possible Values
-     * 1 : RFC Compliant, returns "423 Interval Too Brief"
+     * 1 : RFC Compliant, returns “423 Interval Too Brief�
      * 2 : forces the min_expires value in the subscription
      * 2 : forces the min_expires value in the subscription
 
 
    If > 0 then min_expires_action parameter determines the response.
    If > 0 then min_expires_action parameter determines the response.
 
 
-   Default value is "1".
+   Default value is “1�.
 
 
    Example 1.14. Set min_expires parameter
    Example 1.14. Set min_expires parameter
             ...
             ...
@@ -535,7 +560,7 @@ modparam("presence", "subs_db_mode", 1)
    database or there are other external entities inserting data into the
    database or there are other external entities inserting data into the
    presentity table.
    presentity table.
 
 
-   Default value is "1".
+   Default value is “1�.
 
 
    Example 1.17. Set publ_cache parameter
    Example 1.17. Set publ_cache parameter
 ...
 ...
@@ -548,7 +573,7 @@ modparam("presence", "publ_cache", 0)
    This parameter will be used as the power of 2 when computing table
    This parameter will be used as the power of 2 when computing table
    size.
    size.
 
 
-   Default value is "9 (512)".
+   Default value is “9 (512)�.
 
 
    Example 1.18. Set subs_htable_size parameter
    Example 1.18. Set subs_htable_size parameter
 ...
 ...
@@ -560,7 +585,7 @@ modparam("presence", "subs_htable_size", 11)
    The size of the in-memory hash table to store publish records. This
    The size of the in-memory hash table to store publish records. This
    parameter will be used as the power of 2 when computing table size.
    parameter will be used as the power of 2 when computing table size.
 
 
-   Default value is "9 (512)".
+   Default value is “9 (512)�.
 
 
    Example 1.19. Set pres_htable_size parameter
    Example 1.19. Set pres_htable_size parameter
 ...
 ...
@@ -575,7 +600,7 @@ modparam("presence", "pres_htable_size", 11)
    empty NOTIFY to an message-summary event. This parameter is enabled by
    empty NOTIFY to an message-summary event. This parameter is enabled by
    default, thus addering to the standard.
    default, thus addering to the standard.
 
 
-   Default value is "1 ".
+   Default value is “1 �.
 
 
    Example 1.20. Set send_fast_notify parameter
    Example 1.20. Set send_fast_notify parameter
 ...
 ...
@@ -590,7 +615,7 @@ modparam("presence", "send_fast_notify", 0)
    this check requires extra processing that should be avoided if this
    this check requires extra processing that should be avoided if this
    feature is not supported by the clients.
    feature is not supported by the clients.
 
 
-   Default value is "0 ".
+   Default value is “0 �.
 
 
    Example 1.21. Set enable_sphere_check parameter
    Example 1.21. Set enable_sphere_check parameter
 ...
 ...
@@ -605,7 +630,7 @@ modparam("presence", "enable_sphere_check", 1)
    on. Disabling this will keep subscriptions active on unreliable
    on. Disabling this will keep subscriptions active on unreliable
    networks.
    networks.
 
 
-   Default value is "1".
+   Default value is “1�.
 
 
    Example 1.22. Set timeout_rm_subs parameter
    Example 1.22. Set timeout_rm_subs parameter
 ...
 ...
@@ -703,6 +728,21 @@ if(is_method("PUBLISH")) {
 modparam("presence", "retrieve_order", 1)
 modparam("presence", "retrieve_order", 1)
 ...
 ...
 
 
+3.29. sip_uri_match (int)
+
+   The mode used when comparing uris.
+
+   Possible Values
+     * 0 : case sensitive
+     * 1 : case insensitive
+
+   Default value is “0�.
+
+   Example 1.29. Set sip_uri_match parameter
+            ...
+            modparam("presence", "sip_uri_match", 1)
+            ...
+
 4. Functions
 4. Functions
 
 
    4.1. handle_publish([sender_uri])
    4.1. handle_publish([sender_uri])
@@ -711,7 +751,7 @@ modparam("presence", "retrieve_order", 1)
    4.4. pres_refresh_watchers(uri, event, type[, file_uri, filename])
    4.4. pres_refresh_watchers(uri, event, type[, file_uri, filename])
    4.5. pres_update_watchers(uri, event)
    4.5. pres_update_watchers(uri, event)
 
 
-4.1. handle_publish([sender_uri])
+4.1.  handle_publish([sender_uri])
 
 
    Handles PUBLISH requests by storing and updating published information
    Handles PUBLISH requests by storing and updating published information
    in memory cache and database, then calls functions to send NOTIFY
    in memory cache and database, then calls functions to send NOTIFY
@@ -731,7 +771,7 @@ modparam("presence", "retrieve_order", 1)
 
 
    The module sends an appropriate stateless reply in all cases.
    The module sends an appropriate stateless reply in all cases.
 
 
-   Example 1.29. handle_publish usage
+   Example 1.30. handle_publish usage
 ...
 ...
         if(is_method("PUBLISH"))
         if(is_method("PUBLISH"))
         {
         {
@@ -743,7 +783,7 @@ modparam("presence", "retrieve_order", 1)
         }
         }
 ...
 ...
 
 
-4.2. handle_subscribe([watcher_uri])
+4.2.  handle_subscribe([watcher_uri])
 
 
    The function which handles SUBSCRIBE requests. It stores or updates
    The function which handles SUBSCRIBE requests. It stores or updates
    information in memory and database and calls functions to send NOTIFY
    information in memory and database and calls functions to send NOTIFY
@@ -762,13 +802,13 @@ modparam("presence", "retrieve_order", 1)
 
 
    The module sends an appropriate stateless reply in all cases.
    The module sends an appropriate stateless reply in all cases.
 
 
-   Example 1.30. handle_subscribe usage
+   Example 1.31. handle_subscribe usage
 ...
 ...
 if(method=="SUBSCRIBE")
 if(method=="SUBSCRIBE")
     handle_subscribe();
     handle_subscribe();
 ...
 ...
 
 
-4.3. pres_auth_status(watcher_uri, presentity_uri)
+4.3.  pres_auth_status(watcher_uri, presentity_uri)
 
 
    The function checks if watcher is authorized to subscribe event
    The function checks if watcher is authorized to subscribe event
    'presence' of presentity. Both watcher_uri and presentity_uri are
    'presence' of presentity. Both watcher_uri and presentity_uri are
@@ -779,7 +819,7 @@ if(method=="SUBSCRIBE")
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 
-   Example 1.31. pres_auth_status usage
+   Example 1.32. pres_auth_status usage
 ...
 ...
 if (method=="MESSAGE") {
 if (method=="MESSAGE") {
     pres_auth_status("$fu", $ru");
     pres_auth_status("$fu", $ru");
@@ -791,7 +831,7 @@ if (method=="MESSAGE") {
 }
 }
 ...
 ...
 
 
-4.4. pres_refresh_watchers(uri, event, type[, file_uri, filename])
+4.4.  pres_refresh_watchers(uri, event, type[, file_uri, filename])
 
 
    The function can be used in configuration to triger notifies to
    The function can be used in configuration to triger notifies to
    watchers if a change in watchers authorization or in published state
    watchers if a change in watchers authorization or in published state
@@ -815,12 +855,12 @@ if (method=="MESSAGE") {
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.32. pres_refresh_watchers usage
+   Example 1.33. pres_refresh_watchers usage
 ...
 ...
 pres_refresh_watchers("sip:[email protected]", "presence", 1);
 pres_refresh_watchers("sip:[email protected]", "presence", 1);
 ...
 ...
 
 
-4.5. pres_update_watchers(uri, event)
+4.5.  pres_update_watchers(uri, event)
 
 
    The function can be used in configuration to triger updates to watchers
    The function can be used in configuration to triger updates to watchers
    status if a change in watchers authorization state occurred (i.e.,
    status if a change in watchers authorization state occurred (i.e.,
@@ -833,7 +873,7 @@ pres_refresh_watchers("sip:[email protected]", "presence", 1);
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.33. pres_update_watchers usage
+   Example 1.34. pres_update_watchers usage
 ...
 ...
 pres_update_watchers("sip:[email protected]", "presence");
 pres_update_watchers("sip:[email protected]", "presence");
 ...
 ...
@@ -843,7 +883,7 @@ pres_update_watchers("sip:[email protected]", "presence");
    5.1. refreshWatchers
    5.1. refreshWatchers
    5.2. cleanup
    5.2. cleanup
 
 
-5.1. refreshWatchers
+5.1.  refreshWatchers
 
 
    Triggers sending Notify messages to watchers if a change in watchers
    Triggers sending Notify messages to watchers if a change in watchers
    authorization or in published state occurred.
    authorization or in published state occurred.
@@ -873,7 +913,7 @@ pres_update_watchers("sip:[email protected]", "presence");
                 1
                 1
                 _empty_line_
                 _empty_line_
 
 
-5.2. cleanup
+5.2.  cleanup
 
 
    Manually triggers the cleanup functions for the active_watchers,
    Manually triggers the cleanup functions for the active_watchers,
    presentity, and watchers tables. Useful if you have set clean_period
    presentity, and watchers tables. Useful if you have set clean_period
@@ -891,7 +931,7 @@ pres_update_watchers("sip:[email protected]", "presence");
 
 
    6.1. presence.cleanup
    6.1. presence.cleanup
 
 
-6.1. presence.cleanup
+6.1.  presence.cleanup
 
 
    Manually triggers the cleanup functions for the active_watchers,
    Manually triggers the cleanup functions for the active_watchers,
    presentity, and watchers tables. Useful if you have set clean_period
    presentity, and watchers tables. Useful if you have set clean_period
@@ -901,7 +941,85 @@ pres_update_watchers("sip:[email protected]", "presence");
 
 
    Parameters: none
    Parameters: none
 
 
-7. Installation
+7. Pseudo Variables
+
+   7.1. $subs(attr)
+   7.2. $notify_reply(attr)
+
+7.1. $subs(attr)
+
+   Access the attributes of handled subscription. It must be used after a
+   successful call of “handle_subscription()� or in the following events.
+     * tm:local-request - before notify is sent
+     * present:notify-reply - after notify is sent
+
+   The “attr� can be:
+     * uri - subscription presentity uri
+     * pres_uri - alias for presentity uri
+     * to_user
+     * to_domain
+     * from_user
+     * from_domain
+     * watcher_username
+     * watcher_domain
+     * event
+     * event_id
+     * to_tag
+     * from_tag
+     * callid
+     * remote_cseq
+     * local_cseq
+     * contact
+     * local_contact
+     * record_route
+     * expires
+     * status
+     * reason
+     * version
+     * flags
+     * user_agent
+
+   Example 1.35. $subs(name) usage
+...
+if(handle_subscription())
+{
+  xlog("presentity=$subs(uri)\n");
+}
+...
+
+7.2. $notify_reply(attr)
+
+   Access the reply message received when notifying subscriber. It must be
+   used in the following events.
+     * present:notify-reply - after notify is sent
+
+   The “attr� can be any pseudo var that accesses attributes of msg
+
+   Example 1.36. $notify_reply(name) usage
+...
+event_route[presence:notify-reply]
+{
+  xlog("received message = $notify_reply($mb)\n");
+}
+...
+
+8. Events
+
+   8.1. present:notify-reply
+
+8.1. present:notify-reply
+
+   Fired after notify reply is received or timeout.
+
+   Example 1.37. $notify_reply(name) usage
+...
+event_route[presence:notify-reply]
+{
+  xlog("received message = $notify_reply($mb)\n");
+}
+...
+
+9. Installation
 
 
    The module requires 3 tables in the Kamailio database: "presentity",
    The module requires 3 tables in the Kamailio database: "presentity",
    "active_watchers" and "watchers". The SQL syntax to create them can be
    "active_watchers" and "watchers". The SQL syntax to create them can be
@@ -935,7 +1053,7 @@ Chapter 2. Developer Guide
    The module provides the following functions that can be used in other
    The module provides the following functions that can be used in other
    Kamailio modules.
    Kamailio modules.
 
 
-1. bind_presence(presence_api_t* api)
+1.  bind_presence(presence_api_t* api)
 
 
    This function binds the presence modules and fills the structure with
    This function binds the presence modules and fills the structure with
    one exported function -> add_event, which when called adds a new event
    one exported function -> add_event, which when called adds a new event
@@ -971,7 +1089,7 @@ typedef struct presence_api {
 }presence_api_t;
 }presence_api_t;
 ...
 ...
 
 
-2. add_event
+2.  add_event
 
 
    Field type:
    Field type:
 ...
 ...
@@ -1014,16 +1132,17 @@ typedef struct pres_ev
         free_body_t* free_body;
         free_body_t* free_body;
     /* sometimes it is necessary that a module make changes for a body for each
     /* sometimes it is necessary that a module make changes for a body for each
      * active watcher (e.g. setting the "version" parameter in an XML document.
      * active watcher (e.g. setting the "version" parameter in an XML document.
-     * If a module registers the aux_body_processing callback, it gets called fo
-r
-     * each watcher. It either gets the body received by the PUBLISH, or the bod
-y
+     * If a module registers the aux_body_processing callback, it gets called f
+or
+     * each watcher. It either gets the body received by the PUBLISH, or the bo
+dy
      * generated by the agg_nbody function.
      * generated by the agg_nbody function.
      * The module can deceide if it makes a copy of the original body, which is
      * The module can deceide if it makes a copy of the original body, which is
-then
+ then
      * manipulated, or if it works directly in the original body. If the module
      * manipulated, or if it works directly in the original body. If the module
-makes a
-     * copy of the original body, it also has to register the aux_free_body() to
+ makes a
+     * copy of the original body, it also has to register the aux_free_body() t
+o
      * free this "per watcher" body.
      * free this "per watcher" body.
      */
      */
     aux_body_processing_t* aux_body_processing;
     aux_body_processing_t* aux_body_processing;
@@ -1034,7 +1153,7 @@ makes a
 }pres_ev_t;
 }pres_ev_t;
 ...
 ...
 
 
-3. get_rules_doc
+3.  get_rules_doc
 
 
    Filed type:
    Filed type:
 ...
 ...
@@ -1047,7 +1166,7 @@ typedef int (get_rules_doc_t)(str* user, str* domain, str** rules_doc);
    auth_rules_doc of the subs_t structure given as a parameter to the
    auth_rules_doc of the subs_t structure given as a parameter to the
    functions described bellow.
    functions described bellow.
 
 
-4. get_auth_status
+4.  get_auth_status
 
 
    This filed is a function to be called for a subscription request to
    This filed is a function to be called for a subscription request to
    return the state for that subscription according to authorization
    return the state for that subscription according to authorization
@@ -1062,7 +1181,7 @@ typedef int (get_rules_doc_t)(str* user, str* domain, str** rules_doc);
 typedef int (is_allowed_t)(struct subscription* subs);
 typedef int (is_allowed_t)(struct subscription* subs);
 ...
 ...
 
 
-5. apply_auth_nbody
+5.  apply_auth_nbody
 
 
    This parameter should be a function to be called for an event that
    This parameter should be a function to be called for an event that
    requires authorization, when constructing final body. The authorization
    requires authorization, when constructing final body. The authorization
@@ -1074,7 +1193,7 @@ typedef int (is_allowed_t)(struct subscription* subs);
 typedef int (apply_auth_t)(str* , struct subscription*, str** );
 typedef int (apply_auth_t)(str* , struct subscription*, str** );
 ...
 ...
 
 
-6. agg_nbody
+6.  agg_nbody
 
 
    If present, this field marks that the events requires aggregation of
    If present, this field marks that the events requires aggregation of
    states. This function receives a body array and should return the final
    states. This function receives a body array and should return the final
@@ -1088,7 +1207,7 @@ typedef str* (agg_nbody_t)(str* pres_user, str* pres_domain,
 str** body_array, int n, int off_index);
 str** body_array, int n, int off_index);
 ..
 ..
 
 
-7. free_body
+7.  free_body
 
 
    This field must be field in if subsequent processing is performed on
    This field must be field in if subsequent processing is performed on
    the info from database before being inserted in Notify message body(if
    the info from database before being inserted in Notify message body(if
@@ -1100,7 +1219,7 @@ str** body_array, int n, int off_index);
 typedef void(free_body_t)(char* body);
 typedef void(free_body_t)(char* body);
 ..
 ..
 
 
-8. aux_body_processing
+8.  aux_body_processing
 
 
    This field must be set if the module needs to manipulate the NOTIFY
    This field must be set if the module needs to manipulate the NOTIFY
    body for each watcher. E.g. if the XML body includes a 'version'
    body for each watcher. E.g. if the XML body includes a 'version'
@@ -1114,7 +1233,7 @@ typedef void(free_body_t)(char* body);
 typedef str* (aux_body_processing_t)(struct subscription *subs, str* body);
 typedef str* (aux_body_processing_t)(struct subscription *subs, str* body);
 ..
 ..
 
 
-9. aux_free_body
+9.  aux_free_body
 
 
    This field must be set if the module registers the aux_body_processing
    This field must be set if the module registers the aux_body_processing
    function and allocates memory for the new modified body. Then, this
    function and allocates memory for the new modified body. Then, this
@@ -1129,7 +1248,7 @@ typedef str* (aux_body_processing_t)(struct subscription *subs, str* body);
 typedef void(free_body_t)(char* body);
 typedef void(free_body_t)(char* body);
 ..
 ..
 
 
-10. evs_publ_handl
+10.  evs_publ_handl
 
 
    This function is called when handling Publish requests. Most contain
    This function is called when handling Publish requests. Most contain
    body correctness check.
    body correctness check.
@@ -1138,7 +1257,7 @@ typedef void(free_body_t)(char* body);
 typedef int (publ_handling_t)(struct sip_msg*);
 typedef int (publ_handling_t)(struct sip_msg*);
 ..
 ..
 
 
-11. evs_subs_handl
+11.  evs_subs_handl
 
 
    It is not compulsory. Should contain event specific handling for
    It is not compulsory. Should contain event specific handling for
    Subscription requests.
    Subscription requests.
@@ -1148,7 +1267,7 @@ typedef int (publ_handling_t)(struct sip_msg*);
 typedef int (subs_handling_t)(struct sip_msg*);
 typedef int (subs_handling_t)(struct sip_msg*);
 ..
 ..
 
 
-12. contains_event
+12.  contains_event
 
 
    Field type:
    Field type:
 ..
 ..
@@ -1161,7 +1280,7 @@ event_t* parsed_event);
    found. If the second argument is an allocated event_t* structure it
    found. If the second argument is an allocated event_t* structure it
    fills it with the result of the parsing.
    fills it with the result of the parsing.
 
 
-13. get_event_list
+13.  get_event_list
 
 
    Field type:
    Field type:
 ...
 ...
@@ -1171,7 +1290,7 @@ typedef int (*get_event_list_t) (str** ev_list);
    This function returns a string representation of the events registered
    This function returns a string representation of the events registered
    in presence module.( used for Allowed-Events header).
    in presence module.( used for Allowed-Events header).
 
 
-14. update_watchers_status
+14.  update_watchers_status
 
 
    Field type:
    Field type:
 ...
 ...
@@ -1185,7 +1304,7 @@ str* rules_doc);
    (used by presence_xml module when notified through an MI command of a
    (used by presence_xml module when notified through an MI command of a
    change in an xcap document).
    change in an xcap document).
 
 
-15. get_sphere
+15.  get_sphere
 
 
    Field type:
    Field type:
 ...
 ...
@@ -1196,12 +1315,12 @@ typedef char* (*pres_get_sphere_t)(str* pres_uri);
    information if this has type RPID. If not found returns NULL. (the
    information if this has type RPID. If not found returns NULL. (the
    return value is allocated in private memory and should be freed)
    return value is allocated in private memory and should be freed)
 
 
-16. get_presentity
+16.  get_presentity
 
 
    Field type:
    Field type:
 ...
 ...
-typedef str* (*pres_get_presentity_t)(str pres_uri, pres_ev_t *ev, str *etag, st
-r *contact);
+typedef str* (*pres_get_presentity_t)(str pres_uri, pres_ev_t *ev, str *etag, s
+tr *contact);
 ...
 ...
 
 
    This function returns a pointer to a str containing an XML document
    This function returns a pointer to a str containing an XML document
@@ -1212,7 +1331,7 @@ r *contact);
    Once you are finished with the presentity document you must call
    Once you are finished with the presentity document you must call
    free_presentity to free the allocated memory.
    free_presentity to free the allocated memory.
 
 
-17. free_presentity
+17.  free_presentity
 
 
    Field type:
    Field type:
 ...
 ...

+ 155 - 2
modules/presence/doc/presence_admin.xml

@@ -1136,8 +1136,18 @@ pres_update_watchers("sip:[email protected]", "presence");
 			<title><varname>$subs(attr)</varname></title>
 			<title><varname>$subs(attr)</varname></title>
 			<para>
 			<para>
 				Access the attributes of handled subscription. 
 				Access the attributes of handled subscription. 
-				It must be used after a call of
-				<quote>handle_subscription())</quote>.
+				It must be used after a successful call of
+				<quote>handle_subscription()</quote> or in the following events.
+			<itemizedlist>
+				<listitem>
+				<para><emphasis>tm:local-request</emphasis> - before notify is sent
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>present:notify-reply</emphasis> - after notify is sent
+				</para>
+				</listitem>
+			</itemizedlist>	  
 			</para>
 			</para>
 			<para>
 			<para>
 			The <quote>attr</quote> can be:
 			The <quote>attr</quote> can be:
@@ -1147,6 +1157,98 @@ pres_update_watchers("sip:[email protected]", "presence");
 				<para><emphasis>uri</emphasis> - subscription presentity uri
 				<para><emphasis>uri</emphasis> - subscription presentity uri
 				</para>
 				</para>
 				</listitem>	  
 				</listitem>	  
+				<listitem>
+				<para><emphasis>pres_uri</emphasis> - alias for presentity uri
+				</para>
+				</listitem>	  
+				<listitem>
+				<para><emphasis>to_user</emphasis>
+				</para>
+				</listitem>	  
+				<listitem>
+				<para><emphasis>to_domain</emphasis>
+				</para>
+				</listitem>	  
+				<listitem>
+				<para><emphasis>from_user</emphasis>
+				</para>
+				</listitem>	  
+				<listitem>
+				<para><emphasis>from_domain</emphasis>
+				</para>
+				</listitem>	  
+				<listitem>
+				<para><emphasis>watcher_username</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>watcher_domain</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>event</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>event_id</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>to_tag</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>from_tag</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>callid</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>remote_cseq</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>local_cseq</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>contact</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>local_contact</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>record_route</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>expires</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>status</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>reason</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>version</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>flags</emphasis>
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>user_agent</emphasis>
+				</para>
+				</listitem>
 			</itemizedlist>
 			</itemizedlist>
 
 
 			<example>
 			<example>
@@ -1157,6 +1259,57 @@ if(handle_subscription())
 {
 {
   xlog("presentity=$subs(uri)\n");
   xlog("presentity=$subs(uri)\n");
 }
 }
+...
+				 </programlisting>
+			</example>
+		</section>
+		
+		<section>
+			<title><varname>$notify_reply(attr)</varname></title>
+			<para>
+				Access the reply message received when notifying subscriber. 
+				It must be used in the following events.
+			<itemizedlist>
+				<listitem>
+				<para><emphasis>present:notify-reply</emphasis> - after notify is sent
+				</para>
+				</listitem>
+			</itemizedlist>	  
+			</para>
+			<para>
+			The <quote>attr</quote> can be any pseudo var that accesses attributes of msg
+			</para>
+
+			<example>
+			<title><function moreinfo="none">$notify_reply(name)</function> usage</title>
+<programlisting format="linespecific">
+...
+event_route[presence:notify-reply]
+{
+  xlog("received message = $notify_reply($mb)\n");
+}
+...
+				 </programlisting>
+			</example>
+		</section>
+</section>
+
+<section>
+	<title>Events</title>
+		<section>
+			<title><varname>present:notify-reply</varname></title>
+			<para>
+				Fired after notify reply is received or timeout.
+			</para>
+
+			<example>
+			<title><function moreinfo="none">$notify_reply(name)</function> usage</title>
+<programlisting format="linespecific">
+...
+event_route[presence:notify-reply]
+{
+  xlog("received message = $notify_reply($mb)\n");
+}
 ...
 ...
 				 </programlisting>
 				 </programlisting>
 			</example>
 			</example>

+ 6 - 0
modules/presence/hash.c

@@ -138,6 +138,7 @@ subs_t* mem_copy_subs(subs_t* s, int mem_type)
 		+ s->to_tag.len+ s->from_tag.len+s->sockinfo_str.len+s->event_id.len
 		+ s->to_tag.len+ s->from_tag.len+s->sockinfo_str.len+s->event_id.len
 		+ s->local_contact.len+ s->contact.len+ s->record_route.len
 		+ s->local_contact.len+ s->contact.len+ s->record_route.len
 		+ s->reason.len+ s->watcher_user.len+ s->watcher_domain.len
 		+ s->reason.len+ s->watcher_user.len+ s->watcher_domain.len
+		+ s->user_agent.len
 		+ 1)*sizeof(char);
 		+ 1)*sizeof(char);
 
 
 	if(mem_type & PKG_MEM_TYPE)
 	if(mem_type & PKG_MEM_TYPE)
@@ -166,6 +167,7 @@ subs_t* mem_copy_subs(subs_t* s, int mem_type)
 	CONT_COPY(dest, dest->local_contact, s->local_contact)
 	CONT_COPY(dest, dest->local_contact, s->local_contact)
 	CONT_COPY(dest, dest->contact, s->contact)
 	CONT_COPY(dest, dest->contact, s->contact)
 	CONT_COPY(dest, dest->record_route, s->record_route)
 	CONT_COPY(dest, dest->record_route, s->record_route)
+	CONT_COPY(dest, dest->user_agent, s->user_agent)
 	if(s->event_id.s)
 	if(s->event_id.s)
 		CONT_COPY(dest, dest->event_id, s->event_id)
 		CONT_COPY(dest, dest->event_id, s->event_id)
 	if(s->reason.s)
 	if(s->reason.s)
@@ -179,6 +181,7 @@ subs_t* mem_copy_subs(subs_t* s, int mem_type)
 	dest->send_on_cback= s->send_on_cback;
 	dest->send_on_cback= s->send_on_cback;
 	dest->expires= s->expires;
 	dest->expires= s->expires;
 	dest->db_flag= s->db_flag;
 	dest->db_flag= s->db_flag;
+	dest->flags= s->flags;
 
 
 	return dest;
 	return dest;
 
 
@@ -204,6 +207,7 @@ subs_t* mem_copy_subs_noc(subs_t* s)
 		+ s->to_tag.len+ s->from_tag.len+s->sockinfo_str.len+s->event_id.len
 		+ s->to_tag.len+ s->from_tag.len+s->sockinfo_str.len+s->event_id.len
 		+ s->local_contact.len + s->record_route.len+
 		+ s->local_contact.len + s->record_route.len+
 		+ s->reason.len+ s->watcher_user.len+ s->watcher_domain.len
 		+ s->reason.len+ s->watcher_user.len+ s->watcher_domain.len
+		+ s->user_agent.len
 		+ 1)*sizeof(char);
 		+ 1)*sizeof(char);
 
 
 	dest= (subs_t*)shm_malloc(size);
 	dest= (subs_t*)shm_malloc(size);
@@ -227,6 +231,7 @@ subs_t* mem_copy_subs_noc(subs_t* s)
 	CONT_COPY(dest, dest->sockinfo_str, s->sockinfo_str)
 	CONT_COPY(dest, dest->sockinfo_str, s->sockinfo_str)
 	CONT_COPY(dest, dest->local_contact, s->local_contact)
 	CONT_COPY(dest, dest->local_contact, s->local_contact)
 	CONT_COPY(dest, dest->record_route, s->record_route)
 	CONT_COPY(dest, dest->record_route, s->record_route)
+	CONT_COPY(dest, dest->user_agent, s->user_agent)
 	if(s->event_id.s)
 	if(s->event_id.s)
 		CONT_COPY(dest, dest->event_id, s->event_id)
 		CONT_COPY(dest, dest->event_id, s->event_id)
 	if(s->reason.s)
 	if(s->reason.s)
@@ -240,6 +245,7 @@ subs_t* mem_copy_subs_noc(subs_t* s)
 	dest->send_on_cback= s->send_on_cback;
 	dest->send_on_cback= s->send_on_cback;
 	dest->expires= s->expires;
 	dest->expires= s->expires;
 	dest->db_flag= s->db_flag;
 	dest->db_flag= s->db_flag;
+	dest->flags= s->flags;
 
 
 	dest->contact.s= (char*)shm_malloc(s->contact.len* sizeof(char));
 	dest->contact.s= (char*)shm_malloc(s->contact.len* sizeof(char));
 	if(dest->contact.s== NULL)
 	if(dest->contact.s== NULL)

+ 127 - 30
modules/presence/notify.c

@@ -46,10 +46,13 @@
 #include "presence.h"
 #include "presence.h"
 #include "notify.h"
 #include "notify.h"
 #include "utils_func.h"
 #include "utils_func.h"
+#include "../../receive.h"
 
 
 #define ALLOC_SIZE 3000
 #define ALLOC_SIZE 3000
 #define MAX_FORWARD 70
 #define MAX_FORWARD 70
 
 
+int goto_on_notify_reply=-1;
+
 extern int pres_local_log_level;
 extern int pres_local_log_level;
 
 
 c_back_param* shm_dup_cbparam(subs_t*);
 c_back_param* shm_dup_cbparam(subs_t*);
@@ -95,6 +98,8 @@ str str_sender_col = str_init("sender");
 str str_updated_col = str_init("updated");
 str str_updated_col = str_init("updated");
 str str_updated_winfo_col = str_init("updated_winfo");
 str str_updated_winfo_col = str_init("updated_winfo");
 str str_priority_col = str_init("priority");
 str str_priority_col = str_init("priority");
+str str_flags_col = str_init("flags");
+str str_user_agent_col = str_init("user_agent");
 
 
 int subset=0;
 int subset=0;
 
 
@@ -984,7 +989,7 @@ int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender,
 	db_key_t query_cols[7];
 	db_key_t query_cols[7];
 	db_op_t  query_ops[7];
 	db_op_t  query_ops[7];
 	db_val_t query_vals[7];
 	db_val_t query_vals[7];
-	db_key_t result_cols[19];
+	db_key_t result_cols[21];
 	int n_result_cols = 0, n_query_cols = 0;
 	int n_result_cols = 0, n_query_cols = 0;
 	db_row_t *row ;	
 	db_row_t *row ;	
 	db_val_t *row_vals ;
 	db_val_t *row_vals ;
@@ -995,6 +1000,7 @@ int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender,
 	int version_col= 0, record_route_col = 0, contact_col = 0;
 	int version_col= 0, record_route_col = 0, contact_col = 0;
 	int sockinfo_col= 0, local_contact_col= 0, event_id_col = 0;
 	int sockinfo_col= 0, local_contact_col= 0, event_id_col = 0;
 	int watcher_user_col= 0, watcher_domain_col= 0;
 	int watcher_user_col= 0, watcher_domain_col= 0;
+	int flags_col= 0, user_agent_col= 0;
 	subs_t s, *s_new;
 	subs_t s, *s_new;
 	int inc= 0;
 	int inc= 0;
 		
 		
@@ -1058,6 +1064,8 @@ int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender,
 	result_cols[sockinfo_col=n_result_cols++]     =   &str_socket_info_col;
 	result_cols[sockinfo_col=n_result_cols++]     =   &str_socket_info_col;
 	result_cols[local_contact_col=n_result_cols++]=   &str_local_contact_col;
 	result_cols[local_contact_col=n_result_cols++]=   &str_local_contact_col;
 	result_cols[version_col=n_result_cols++]      =   &str_version_col;
 	result_cols[version_col=n_result_cols++]      =   &str_version_col;
+	result_cols[flags_col=n_result_cols++]        =   &str_flags_col;
+	result_cols[user_agent_col=n_result_cols++]   =   &str_user_agent_col;
 
 
 	if (pa_dbf.query(pa_db, query_cols, query_ops, query_vals,result_cols,
 	if (pa_dbf.query(pa_db, query_cols, query_ops, query_vals,result_cols,
 				n_query_cols, n_result_cols, 0, &result) < 0) 
 				n_query_cols, n_result_cols, 0, &result) < 0) 
@@ -1148,6 +1156,9 @@ int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender,
 		else
 		else
 		    s.expires = row_vals[expires_col].val.int_val - (int)time(NULL);
 		    s.expires = row_vals[expires_col].val.int_val - (int)time(NULL);
 		s.version = row_vals[version_col].val.int_val +1;
 		s.version = row_vals[version_col].val.int_val +1;
+		s.flags = row_vals[flags_col].val.int_val;
+		s.user_agent.s=  (char*)row_vals[user_agent_col].val.string_val;
+		s.user_agent.len= (s.user_agent.s)?strlen(s.user_agent.s):0;
 
 
 		s_new= mem_copy_subs(&s, PKG_MEM_TYPE);
 		s_new= mem_copy_subs(&s, PKG_MEM_TYPE);
 		if(s_new== NULL)
 		if(s_new== NULL)
@@ -1467,7 +1478,7 @@ int send_notify_request(subs_t* subs, subs_t * watcher_subs,
 	str str_hdr = {0, 0};
 	str str_hdr = {0, 0};
 	str* notify_body = NULL;
 	str* notify_body = NULL;
 	int result= 0;
 	int result= 0;
-	c_back_param *cb_param= NULL;
+	subs_t *cb_param= NULL;
 	str* final_body= NULL;
 	str* final_body= NULL;
 	uac_req_t uac_r;
 	uac_req_t uac_r;
 	str* aux_body = NULL;
 	str* aux_body = NULL;
@@ -1589,16 +1600,7 @@ jump_over_body:
 	}
 	}
 
 
 	LM_DBG("expires %d status %d\n", subs->expires, subs->status);
 	LM_DBG("expires %d status %d\n", subs->expires, subs->status);
-	/* if status is TERMINATED_STATUS, the subscription will be deleted so no need to send a parameter */
-	if(subs->status != TERMINATED_STATUS)
-	{
-		cb_param = shm_dup_cbparam(subs);
-		if(cb_param == NULL)
-		{
-			LM_ERR("while duplicating cb_param in share memory\n");
-			goto error;
-		}
-	}
+	cb_param = mem_copy_subs(subs, SHM_MEM_TYPE);
 
 
 	set_uac_req(&uac_r, &met, &str_hdr, notify_body, td, TMCB_LOCAL_COMPLETED,
 	set_uac_req(&uac_r, &met, &str_hdr, notify_body, td, TMCB_LOCAL_COMPLETED,
 			p_tm_callback, (void*)cb_param);
 			p_tm_callback, (void*)cb_param);
@@ -1607,7 +1609,7 @@ jump_over_body:
 	{
 	{
 		LM_ERR("in function tmb.t_request_within\n");
 		LM_ERR("in function tmb.t_request_within\n");
 		if(cb_param)
 		if(cb_param)
-			free_cbparam(cb_param);
+			shm_free(cb_param);
 		goto error;
 		goto error;
 	}
 	}
 
 
@@ -1700,30 +1702,120 @@ int notify(subs_t* subs, subs_t * watcher_subs,str* n_body,int force_null_body)
 	return 0;
 	return 0;
 }
 }
 
 
-void p_tm_callback( struct cell *t, int type, struct tmcb_params *ps)
+extern subs_t* _pres_subs_last_sub;
+sip_msg_t* _pres_subs_notify_reply_msg = NULL;
+int _pres_subs_notify_reply_code = 0;
+
+int pv_parse_notify_reply_var_name(pv_spec_p sp, str *in)
+{
+	pv_spec_t *pv=NULL;
+	if(in->s==NULL || in->len<=0)
+		return -1;
+	pv = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t));
+	if(pv==NULL)
+		return -1;
+	memset(pv, 0, sizeof(pv_spec_t));
+	if(pv_parse_spec(in, pv)==NULL)
+		goto error;
+	sp->pvp.pvn.u.dname = (void*)pv;
+	sp->pvp.pvn.type = PV_NAME_PVAR;
+	return 0;
+
+error:
+	LM_ERR("invalid pv name [%.*s]\n", in->len, in->s);
+	if(pv!=NULL)
+		pkg_free(pv);
+	return -1;
+}
+
+int pv_get_notify_reply(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res)
 {
 {
-	c_back_param*  cb;
+	pv_spec_t *pv=NULL;
 
 
-	if(ps->param==NULL || *ps->param==NULL ||
-			((c_back_param*)(*ps->param))->callid.s == NULL ||
-			((c_back_param*)(*ps->param))->to_tag.s== NULL ||
-			((c_back_param*)(*ps->param))->from_tag.s== NULL)
-	{
-		LM_DBG("message id not received, probably a timeout notify\n");
-		if(ps->param != NULL && *ps->param !=NULL)
-			free_cbparam((c_back_param*)(*ps->param));
+	if(msg==NULL)
+		return 1;
+
+	pv = (pv_spec_t*)param->pvn.u.dname;
+	if(pv==NULL)
+		return pv_get_null(msg, param, res);
+
+	return pv_get_spec_value(_pres_subs_notify_reply_msg, pv, res);
+}
+
+#define FAKED_SIP_408_MSG_FORMAT "SIP/2.0 408 TIMEOUT\r\nVia: SIP/2.0/UDP 127.0.0.1\r\nFrom: invalid;\r\nTo: invalid\r\nCall-ID: invalid\r\nCSeq: 1 TIMEOUT\r\nContent-Length: 0\r\n\r\n"
+static sip_msg_t* _faked_msg = NULL;
+
+sip_msg_t* faked_msg() {
+	if(_faked_msg == NULL) {
+		_faked_msg = pkg_malloc(sizeof(sip_msg_t));
+		if(likely(build_sip_msg_from_buf(_faked_msg, FAKED_SIP_408_MSG_FORMAT, strlen(FAKED_SIP_408_MSG_FORMAT), inc_msg_no())<0)) {
+			LM_ERR("failed to parse msg buffer\n");
+			return NULL;
+		}
+	}
+	return _faked_msg;
+}
+
+void run_notify_reply_event(struct cell *t, struct tmcb_params *ps)
+{
+	int backup_route_type;
+	subs_t* backup_subs = NULL;
+	sip_msg_t msg;
+
+	if (goto_on_notify_reply==-1)
+		return;
+
+	if(likely(build_sip_msg_from_buf(&msg, t->uac->request.buffer, t->uac->request.buffer_len, inc_msg_no())<0)) {
+		LM_ERR("failed to parse msg buffer\n");
 		return;
 		return;
 	}
 	}
 
 
-	cb= (c_back_param*)(*ps->param);
+	_pres_subs_notify_reply_code = ps->code;
+	if( ps->code == 408 || ps->rpl == NULL) {
+		_pres_subs_notify_reply_msg = faked_msg();
+	} else {
+		_pres_subs_notify_reply_msg = ps->rpl;
+	}
+
+	backup_subs = _pres_subs_last_sub;
+	_pres_subs_last_sub = mem_copy_subs((subs_t*)(*ps->param), PKG_MEM_TYPE);
+
+	backup_route_type = get_route_type();
+	set_route_type(LOCAL_ROUTE);
+	run_top_route(event_rt.rlist[goto_on_notify_reply], &msg, 0);
+	set_route_type(backup_route_type);
+
+	_pres_subs_notify_reply_msg = NULL;
+	_pres_subs_notify_reply_code = 0;
+	pkg_free(_pres_subs_last_sub);
+	_pres_subs_last_sub = backup_subs;
+	free_sip_msg(&msg);
+
+}
+
+void p_tm_callback( struct cell *t, int type, struct tmcb_params *ps)
+{
+	subs_t* subs;
+
+	if(ps->param == NULL || *ps->param == NULL) {
+                LM_ERR("weird shit happening\n");
+                if(ps->param != NULL && *ps->param !=NULL)
+                        shm_free((subs_t*)(*ps->param));
+                return;
+	}
+
+	subs= (subs_t*)(*ps->param);
 	LM_DBG("completed with status %d [to_tag:%.*s]\n",
 	LM_DBG("completed with status %d [to_tag:%.*s]\n",
-			ps->code, cb->to_tag.len, cb->to_tag.s);
+			ps->code, subs->to_tag.len, subs->to_tag.s);
+
+	run_notify_reply_event(t, ps);
 
 
-	if(ps->code == 481 || (ps->code == 408 && timeout_rm_subs))
-		delete_subs(&cb->pres_uri, &cb->ev_name,
-				&cb->to_tag, &cb->from_tag, &cb->callid);
+	if(ps->code == 404 || ps->code == 481 || (ps->code == 408 && timeout_rm_subs)) {
+		delete_subs(&subs->pres_uri, &subs->event->name,
+				&subs->to_tag, &subs->from_tag, &subs->callid);
+	}
 
 
-	free_cbparam(cb);
+	shm_free(subs);
 }
 }
 
 
 void free_cbparam(c_back_param* cb_param)
 void free_cbparam(c_back_param* cb_param)
@@ -2667,7 +2759,7 @@ error:
 
 
 int process_dialogs(int round, int presence_winfo)
 int process_dialogs(int round, int presence_winfo)
 {
 {
-	db_key_t query_cols[3], result_cols[18], update_cols[4];
+	db_key_t query_cols[3], result_cols[20], update_cols[4];
 	db_val_t query_vals[3], update_vals[4], *values, *dvalues;
 	db_val_t query_vals[3], update_vals[4], *values, *dvalues;
 	db_op_t query_ops[2];
 	db_op_t query_ops[2];
 	db_row_t *rows, *drows;
 	db_row_t *rows, *drows;
@@ -2678,6 +2770,7 @@ int process_dialogs(int round, int presence_winfo)
 	int wuser_col, wdomain_col, sockinfo_col, lcontact_col, contact_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 rroute_col, event_id_col, reason_col, event_col, lcseq_col;
 	int rcseq_col, status_col, version_col, updated_winfo_col, expires_col;
 	int rcseq_col, status_col, version_col, updated_winfo_col, expires_col;
+	int flags_col, user_agent_col;
 	int i, notify_sent = 0, cached_updated_winfo, ret = -1;
 	int i, notify_sent = 0, cached_updated_winfo, ret = -1;
 	int end_transaction = 0;
 	int end_transaction = 0;
 	subs_t sub;
 	subs_t sub;
@@ -2818,6 +2911,8 @@ int process_dialogs(int round, int presence_winfo)
 		result_cols[version_col = n_result_cols++] = &str_version_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[updated_winfo_col = n_result_cols++] = &str_updated_winfo_col;
 		result_cols[expires_col = n_result_cols++] = &str_expires_col;
 		result_cols[expires_col = n_result_cols++] = &str_expires_col;
+		result_cols[flags_col = n_result_cols++] = &str_flags_col;
+		result_cols[user_agent_col = n_result_cols++] = &str_user_agent_col;
 
 
 		/* Need to redo this here as we might have switched to the
 		/* Need to redo this here as we might have switched to the
 		   presentity table during a previous iteration. */
 		   presentity table during a previous iteration. */
@@ -2885,6 +2980,7 @@ int process_dialogs(int round, int presence_winfo)
 		EXTRACT_STRING(sub.record_route, VAL_STRING(&dvalues[rroute_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.event_id, VAL_STRING(&dvalues[event_id_col]));
 		EXTRACT_STRING(sub.reason, VAL_STRING(&dvalues[reason_col]));
 		EXTRACT_STRING(sub.reason, VAL_STRING(&dvalues[reason_col]));
+		EXTRACT_STRING(sub.user_agent, VAL_STRING(&dvalues[user_agent_col]));
 
 
 		sub.local_cseq = VAL_INT(&dvalues[lcseq_col]) + 1;
 		sub.local_cseq = VAL_INT(&dvalues[lcseq_col]) + 1;
 		sub.remote_cseq = VAL_INT(&dvalues[rcseq_col]);
 		sub.remote_cseq = VAL_INT(&dvalues[rcseq_col]);
@@ -2897,6 +2993,7 @@ int process_dialogs(int round, int presence_winfo)
 			sub.expires = VAL_INT(&dvalues[expires_col]) - now;
 			sub.expires = VAL_INT(&dvalues[expires_col]) - now;
 		else
 		else
 			sub.expires = 0;
 			sub.expires = 0;
+		sub.flags = VAL_INT(&dvalues[flags_col]);
 
 
 		sub.updated = round;
 		sub.updated = round;
 
 

+ 6 - 0
modules/presence/notify.h

@@ -97,6 +97,12 @@ extern str str_sender_col;
 extern str str_updated_col;
 extern str str_updated_col;
 extern str str_updated_winfo_col;
 extern str str_updated_winfo_col;
 extern str str_priority_col;
 extern str str_priority_col;
+extern str str_flags_col;
+extern str str_user_agent_col;
+
+extern int goto_on_notify_reply;
+int pv_parse_notify_reply_var_name(pv_spec_p sp, str *in);
+int pv_get_notify_reply(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res);
 
 
 void PRINT_DLG(FILE* out, dlg_t* _d);
 void PRINT_DLG(FILE* out, dlg_t* _d);
 
 

+ 16 - 2
modules/presence/presence.c

@@ -77,7 +77,7 @@ MODULE_VERSION
 
 
 #define S_TABLE_VERSION  3
 #define S_TABLE_VERSION  3
 #define P_TABLE_VERSION  4
 #define P_TABLE_VERSION  4
-#define ACTWATCH_TABLE_VERSION 11
+#define ACTWATCH_TABLE_VERSION 12
 
 
 char *log_buf = NULL;
 char *log_buf = NULL;
 static int clean_period=100;
 static int clean_period=100;
@@ -230,6 +230,7 @@ static mi_export_t mi_cmds[] = {
 
 
 static pv_export_t pres_mod_pvs[] = {
 static pv_export_t pres_mod_pvs[] = {
 	{{"subs", (sizeof("subs")-1)}, PVT_OTHER, pv_get_subscription, 0, pv_parse_subscription_name, 0, 0, 0},
 	{{"subs", (sizeof("subs")-1)}, PVT_OTHER, pv_get_subscription, 0, pv_parse_subscription_name, 0, 0, 0},
+	{{"notify_reply", (sizeof("notify_reply")-1)}, PVT_OTHER, pv_get_notify_reply, 0, pv_parse_notify_reply_var_name, 0, 0, 0},
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 };
 };
 
 
@@ -440,6 +441,10 @@ static int mod_init(void)
 	pa_dbf.close(pa_db);
 	pa_dbf.close(pa_db);
 	pa_db = NULL;
 	pa_db = NULL;
 
 
+	goto_on_notify_reply=route_lookup(&event_rt, "presence:notify-reply");
+	if (goto_on_notify_reply>=0 && event_rt.rlist[goto_on_notify_reply]==0)
+		goto_on_notify_reply=-1; /* disable */
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1220,7 +1225,7 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 {
 {
 	db_key_t query_cols[5], db_cols[3];
 	db_key_t query_cols[5], db_cols[3];
 	db_val_t query_vals[5], db_vals[3];
 	db_val_t query_vals[5], db_vals[3];
-	db_key_t result_cols[24];
+	db_key_t result_cols[26];
 	int n_query_cols=0, n_result_cols=0, n_update_cols=0;
 	int n_query_cols=0, n_result_cols=0, n_update_cols=0;
 	int event_col, pres_uri_col, watcher_user_col, watcher_domain_col;
 	int event_col, pres_uri_col, watcher_user_col, watcher_domain_col;
 	int r_pres_uri_col,r_to_user_col,r_to_domain_col;
 	int r_pres_uri_col,r_to_user_col,r_to_domain_col;
@@ -1231,6 +1236,7 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 	int r_event_col, r_local_cseq_col, r_remote_cseq_col;
 	int r_event_col, r_local_cseq_col, r_remote_cseq_col;
 	int r_status_col, r_version_col;
 	int r_status_col, r_version_col;
 	int r_expires_col, r_watcher_user_col, r_watcher_domain_col;
 	int r_expires_col, r_watcher_user_col, r_watcher_domain_col;
+	int r_flags_col, r_user_agent_col;
 	db1_res_t *result= NULL;
 	db1_res_t *result= NULL;
  	db_val_t *row_vals;
  	db_val_t *row_vals;
 	db_row_t *rows;
 	db_row_t *rows;
@@ -1301,6 +1307,9 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 	result_cols[r_status_col=n_result_cols++] = &str_status_col;
 	result_cols[r_status_col=n_result_cols++] = &str_status_col;
 	/*********************************************/
 	/*********************************************/
 
 
+	result_cols[r_flags_col=n_result_cols++] = &str_flags_col;
+	result_cols[r_user_agent_col=n_result_cols++] = &str_user_agent_col;
+
 	if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols, 
 	if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols, 
 				n_query_cols, n_result_cols, 0, &result )< 0)
 				n_query_cols, n_result_cols, 0, &result )< 0)
 	{
 	{
@@ -1399,6 +1408,11 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 
 
 		s.version = row_vals[r_version_col].val.int_val;
 		s.version = row_vals[r_version_col].val.int_val;
 
 
+		s.flags = row_vals[r_flags_col].val.int_val;
+		s.user_agent.s=  (char*)row_vals[r_user_agent_col].val.string_val;
+		s.user_agent.len= (s.user_agent.s)?strlen(s.user_agent.s):0;
+
+
 		cs = mem_copy_subs(&s, PKG_MEM_TYPE);
 		cs = mem_copy_subs(&s, PKG_MEM_TYPE);
 		if (cs == NULL)
 		if (cs == NULL)
 		{
 		{

+ 251 - 41
modules/presence/subscribe.c

@@ -40,6 +40,7 @@
 #include "notify.h"
 #include "notify.h"
 #include "../pua/hash.h"
 #include "../pua/hash.h"
 #include "../../mod_fix.h"
 #include "../../mod_fix.h"
+#include "../../dset.h"
 
 
 int get_stored_info(struct sip_msg* msg, subs_t* subs, int* error_ret,
 int get_stored_info(struct sip_msg* msg, subs_t* subs, int* error_ret,
 		str* reply_str);
 		str* reply_str);
@@ -181,14 +182,15 @@ int delete_db_subs(str* to_tag, str* from_tag, str* callid)
 
 
 int insert_subs_db(subs_t* s, int type)
 int insert_subs_db(subs_t* s, int type)
 {
 {
-	db_key_t query_cols[24];
-	db_val_t query_vals[24];
+	db_key_t query_cols[26];
+	db_val_t query_vals[26];
 	int n_query_cols = 0;
 	int n_query_cols = 0;
 	int pres_uri_col, to_user_col, to_domain_col, from_user_col, from_domain_col,
 	int pres_uri_col, to_user_col, to_domain_col, from_user_col, from_domain_col,
 		callid_col, totag_col, fromtag_col, event_col,status_col, event_id_col, 
 		callid_col, totag_col, fromtag_col, event_col,status_col, event_id_col, 
 		local_cseq_col, remote_cseq_col, expires_col, record_route_col, 
 		local_cseq_col, remote_cseq_col, expires_col, record_route_col, 
 		contact_col, local_contact_col, version_col,socket_info_col,reason_col,
 		contact_col, local_contact_col, version_col,socket_info_col,reason_col,
-		watcher_user_col, watcher_domain_col, updated_col, updated_winfo_col;
+		watcher_user_col, watcher_domain_col, updated_col, updated_winfo_col,
+		user_agent_col, flags_col;
 		
 		
 	if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0)
 	if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0)
 	{
 	{
@@ -316,6 +318,16 @@ int insert_subs_db(subs_t* s, int type)
 	query_vals[updated_winfo_col].nul = 0;
 	query_vals[updated_winfo_col].nul = 0;
 	n_query_cols++;
 	n_query_cols++;
 
 
+	query_cols[flags_col= n_query_cols]=&str_flags_col;
+	query_vals[flags_col].type = DB1_INT;
+	query_vals[flags_col].nul = 0;
+	n_query_cols++;
+
+	query_cols[user_agent_col= n_query_cols]=&str_user_agent_col;
+	query_vals[user_agent_col].type = DB1_STR;
+	query_vals[user_agent_col].nul = 0;
+	n_query_cols++;
+
 	query_vals[pres_uri_col].val.str_val= s->pres_uri;
 	query_vals[pres_uri_col].val.str_val= s->pres_uri;
 	query_vals[callid_col].val.str_val= s->callid;
 	query_vals[callid_col].val.str_val= s->callid;
 	query_vals[totag_col].val.str_val= s->to_tag;
 	query_vals[totag_col].val.str_val= s->to_tag;
@@ -340,6 +352,8 @@ int insert_subs_db(subs_t* s, int type)
 	query_vals[socket_info_col].val.str_val= s->sockinfo_str;
 	query_vals[socket_info_col].val.str_val= s->sockinfo_str;
 	query_vals[updated_col].val.int_val = s->updated;
 	query_vals[updated_col].val.int_val = s->updated;
 	query_vals[updated_winfo_col].val.int_val = s->updated_winfo;
 	query_vals[updated_winfo_col].val.int_val = s->updated_winfo;
+	query_vals[flags_col].val.int_val = s->flags;
+	query_vals[user_agent_col].val.str_val= s->user_agent;
 
 
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
 	{
 	{
@@ -755,7 +769,37 @@ void msg_watchers_clean(unsigned int ticks,void *param)
 		LM_ERR("cleaning pending subscriptions\n");
 		LM_ERR("cleaning pending subscriptions\n");
 }
 }
 
 
-static char* _pres_subs_last_presentity = NULL;
+subs_t* _pres_subs_last_sub = NULL;
+
+/*
+ * Map between $subs(idxname) and subs_t
+ *
+ * uri (pres_uri) 1
+ * pres_uri 1
+ * to_user 2
+ * to_domain 3
+ * from_user 4
+ * from_domain 5
+ * watcher_username 6
+ * watcher_domain 7
+ * event (event->name) 8
+ * event_id 9
+ * to_tag 10
+ * from_tag 11
+ * callid 12
+ * remote_cseq 13
+ * local_cseq 14
+ * contact 15
+ * local_contact 16
+ * record_route 17
+ * expires 18
+ * status 19
+ * reason 20
+ * version 21
+ * flags 22
+ * user_agent 23
+ *
+ */
 
 
 int pv_parse_subscription_name(pv_spec_p sp, str *in)
 int pv_parse_subscription_name(pv_spec_p sp, str *in)
 {
 {
@@ -771,6 +815,119 @@ int pv_parse_subscription_name(pv_spec_p sp, str *in)
 				goto error;
 				goto error;
 			};
 			};
 			break;
 			break;
+
+		case 5:
+			if(strncmp(in->s, "event", 5)==0) {
+				sp->pvp.pvn.u.isname.name.n = 8;
+			} else if(strncmp(in->s, "flags", 5)==0) {
+				sp->pvp.pvn.u.isname.name.n = 22;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 6:
+			if(strncmp(in->s, "to_tag", 6)==0) {
+				sp->pvp.pvn.u.isname.name.n = 10;
+			} else if(strncmp(in->s, "callid", 6)==0) {
+				sp->pvp.pvn.u.isname.name.n = 12;
+			} else if(strncmp(in->s, "status", 6)==0) {
+				sp->pvp.pvn.u.isname.name.n = 19;
+			} else if(strncmp(in->s, "reason", 6)==0) {
+				sp->pvp.pvn.u.isname.name.n = 20;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 7:
+			if(strncmp(in->s, "to_user", 7)==0) {
+				sp->pvp.pvn.u.isname.name.n = 2;
+			} else if(strncmp(in->s, "contact", 7)==0) {
+				sp->pvp.pvn.u.isname.name.n = 15;
+			} else if(strncmp(in->s, "expires", 7)==0) {
+				sp->pvp.pvn.u.isname.name.n = 18;
+			} else if(strncmp(in->s, "version", 7)==0) {
+				sp->pvp.pvn.u.isname.name.n = 21;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 8:
+			if(strncmp(in->s, "pres_uri", 8)==0) {
+				sp->pvp.pvn.u.isname.name.n = 1;
+			} else if(strncmp(in->s, "event_id", 8)==0) {
+				sp->pvp.pvn.u.isname.name.n = 9;
+			} else if(strncmp(in->s, "from_tag", 8)==0) {
+				sp->pvp.pvn.u.isname.name.n = 11;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 9:
+			if(strncmp(in->s, "to_domain", 9)==0) {
+				sp->pvp.pvn.u.isname.name.n = 3;
+			} else if(strncmp(in->s, "from_user", 9)==0) {
+				sp->pvp.pvn.u.isname.name.n = 4;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 10:
+			if(strncmp(in->s, "local_cseq", 10)==0) {
+				sp->pvp.pvn.u.isname.name.n = 14;
+			} else if(strncmp(in->s, "user_agent", 10)==0) {
+				sp->pvp.pvn.u.isname.name.n = 23;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 11:
+			if(strncmp(in->s, "from_domain", 11)==0) {
+				sp->pvp.pvn.u.isname.name.n = 5;
+			} else if(strncmp(in->s, "remote_cseq", 11)==0) {
+				sp->pvp.pvn.u.isname.name.n = 13;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 12:
+			if(strncmp(in->s, "record_route", 12)==0) {
+				sp->pvp.pvn.u.isname.name.n = 17;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 13:
+			if(strncmp(in->s, "local_contact", 13)==0) {
+				sp->pvp.pvn.u.isname.name.n = 16;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 14:
+			if(strncmp(in->s, "watcher_domain", 14)==0) {
+				sp->pvp.pvn.u.isname.name.n = 7;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 16:
+			if(strncmp(in->s, "watcher_username", 16)==0) {
+				sp->pvp.pvn.u.isname.name.n = 6;
+			} else {
+				goto error;
+			};
+			break;
+
 		default:
 		default:
 			goto error;
 			goto error;
 	}
 	}
@@ -786,13 +943,60 @@ error:
 
 
 int pv_get_subscription(struct sip_msg *msg, pv_param_t *param,	pv_value_t *res)
 int pv_get_subscription(struct sip_msg *msg, pv_param_t *param,	pv_value_t *res)
 {
 {
-	if(param->pvn.u.isname.name.n==1) /* presentity */
-		return (_pres_subs_last_presentity == NULL) ? pv_get_null(msg, param, res)
-						: pv_get_strzval(msg, param, res, _pres_subs_last_presentity);
+	if(_pres_subs_last_sub == NULL) {
+		return pv_get_null(msg, param, res);
+	}
+
+	if(param->pvn.u.isname.name.n==1) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->pres_uri);
+	} else if(param->pvn.u.isname.name.n==2) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->to_user);
+	} else if(param->pvn.u.isname.name.n==3) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->to_domain);
+	} else if(param->pvn.u.isname.name.n==4) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->from_user);
+	} else if(param->pvn.u.isname.name.n==5) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->from_domain);
+	} else if(param->pvn.u.isname.name.n==6) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->watcher_user);
+	} else if(param->pvn.u.isname.name.n==7) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->watcher_domain);
+	} else if(param->pvn.u.isname.name.n==8) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->event->name);
+	} else if(param->pvn.u.isname.name.n==9) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->event_id);
+	} else if(param->pvn.u.isname.name.n==10) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->to_tag);
+	} else if(param->pvn.u.isname.name.n==11) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->from_tag);
+	} else if(param->pvn.u.isname.name.n==12) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->callid);
+	} else if(param->pvn.u.isname.name.n==13) {
+		return pv_get_uintval(msg, param, res, _pres_subs_last_sub->remote_cseq);
+	} else if(param->pvn.u.isname.name.n==14) {
+		return pv_get_uintval(msg, param, res, _pres_subs_last_sub->local_cseq);
+	} else if(param->pvn.u.isname.name.n==15) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->contact);
+	} else if(param->pvn.u.isname.name.n==16) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->local_contact);
+	} else if(param->pvn.u.isname.name.n==17) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->record_route);
+	} else if(param->pvn.u.isname.name.n==18) {
+		return pv_get_uintval(msg, param, res, _pres_subs_last_sub->expires);
+	} else if(param->pvn.u.isname.name.n==19) {
+		return pv_get_uintval(msg, param, res, _pres_subs_last_sub->status);
+	} else if(param->pvn.u.isname.name.n==20) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->reason);
+	} else if(param->pvn.u.isname.name.n==21) {
+		return pv_get_sintval(msg, param, res, _pres_subs_last_sub->version);
+	} else if(param->pvn.u.isname.name.n==22) {
+		return pv_get_sintval(msg, param, res, _pres_subs_last_sub->flags);
+	} else if(param->pvn.u.isname.name.n==23) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->user_agent);
+	}
 
 
 	LM_ERR("unknown specifier\n");
 	LM_ERR("unknown specifier\n");
 	return pv_get_null(msg, param, res);
 	return pv_get_null(msg, param, res);
-
 }
 }
 
 
 int handle_subscribe0(struct sip_msg* msg)
 int handle_subscribe0(struct sip_msg* msg)
@@ -849,9 +1053,9 @@ int handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_domain)
 	str reply_str;
 	str reply_str;
 	int sent_reply= 0;
 	int sent_reply= 0;
 
 
-	if(_pres_subs_last_presentity) {
-		pkg_free(_pres_subs_last_presentity);
-		_pres_subs_last_presentity = NULL;
+	if(_pres_subs_last_sub) {
+		pkg_free(_pres_subs_last_sub);
+		_pres_subs_last_sub = NULL;
 	}
 	}
 
 
 	/* ??? rename to avoid collisions with other symbols */
 	/* ??? rename to avoid collisions with other symbols */
@@ -952,13 +1156,6 @@ int handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_domain)
 		reason= subs.reason;
 		reason= subs.reason;
 	}
 	}
 
 
-	if(subs.pres_uri.len > 0 && subs.pres_uri.s) {
-		_pres_subs_last_presentity =
-				(char*)pkg_malloc((subs.pres_uri.len+1) * sizeof(char));
-		strncpy(_pres_subs_last_presentity, subs.pres_uri.s, subs.pres_uri.len);
-		_pres_subs_last_presentity[subs.pres_uri.len] = '\0';
-	}
-		
 	/* mark that the received event is a SUBSCRIBE message */
 	/* mark that the received event is a SUBSCRIBE message */
 	subs.recv_event = PRES_SUBSCRIBE_RECV;
 	subs.recv_event = PRES_SUBSCRIBE_RECV;
 
 
@@ -1033,6 +1230,8 @@ int handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_domain)
 		}
 		}
 	}
 	}
 
 
+	_pres_subs_last_sub = mem_copy_subs(&subs, PKG_MEM_TYPE);
+
 	/* check if correct status */
 	/* check if correct status */
 	if(get_status_str(subs.status)== NULL)
 	if(get_status_str(subs.status)== NULL)
 	{
 	{
@@ -1378,6 +1577,12 @@ int extract_sdialog_info_ex(subs_t* subs,struct sip_msg* msg, int miexp,
 	else
 	else
 		subs->local_contact= scontact;
 		subs->local_contact= scontact;
 
 
+	if (parse_headers(msg, HDR_USERAGENT_F, 0) != -1 && msg->user_agent &&
+			msg->user_agent->body.len>0 && msg->user_agent->body.len<MAX_UA_SIZE) {
+		subs->user_agent = msg->user_agent->body;
+	}
+	getbflagsval(0, &subs->flags);
+
 	free_to_params(&TO);
 	free_to_params(&TO);
 	return 0;
 	return 0;
 	
 	
@@ -1534,7 +1739,7 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 {
 {
 	db_key_t query_cols[3];
 	db_key_t query_cols[3];
 	db_val_t query_vals[3];
 	db_val_t query_vals[3];
-	db_key_t result_cols[9];
+	db_key_t result_cols[10];
 	db1_res_t *result= NULL;
 	db1_res_t *result= NULL;
 	db_row_t *row ;	
 	db_row_t *row ;	
 	db_val_t *row_vals ;
 	db_val_t *row_vals ;
@@ -1543,6 +1748,7 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	int remote_cseq_col= 0, local_cseq_col= 0, status_col, reason_col;
 	int remote_cseq_col= 0, local_cseq_col= 0, status_col, reason_col;
 	int record_route_col, version_col, pres_uri_col;
 	int record_route_col, version_col, pres_uri_col;
 	int updated_col, updated_winfo_col;
 	int updated_col, updated_winfo_col;
+	int flags_col;
 	unsigned int remote_cseq;
 	unsigned int remote_cseq;
 	str pres_uri, record_route;
 	str pres_uri, record_route;
 	str reason;
 	str reason;
@@ -1575,6 +1781,7 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	result_cols[version_col=n_result_cols++] = &str_version_col;
 	result_cols[version_col=n_result_cols++] = &str_version_col;
 	result_cols[updated_col=n_result_cols++] = &str_updated_col;
 	result_cols[updated_col=n_result_cols++] = &str_updated_col;
 	result_cols[updated_winfo_col=n_result_cols++] = &str_updated_winfo_col;
 	result_cols[updated_winfo_col=n_result_cols++] = &str_updated_winfo_col;
+	result_cols[flags_col=n_result_cols++] = &str_flags_col;
 	
 	
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) 
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) 
 	{
 	{
@@ -1626,13 +1833,14 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	if(reason.s)
 	if(reason.s)
 	{
 	{
 		reason.len= strlen(reason.s);
 		reason.len= strlen(reason.s);
-		subs->reason.s= (char*)pkg_malloc(reason.len* sizeof(char));
-		if(subs->reason.s== NULL)
-		{
-			ERR_MEM(PKG_MEM_STR);
+		if(reason.len > 0) {
+			subs->reason.s= (char*)pkg_malloc(reason.len* sizeof(char));
+			if(subs->reason.s == NULL) {
+				ERR_MEM(PKG_MEM_STR);
+			}
+			memcpy(subs->reason.s, reason.s, reason.len);
+			subs->reason.len= reason.len;
 		}
 		}
-		memcpy(subs->reason.s, reason.s, reason.len);
-		subs->reason.len= reason.len;
 	}
 	}
 
 
 	subs->local_cseq= row_vals[local_cseq_col].val.int_val + 1;
 	subs->local_cseq= row_vals[local_cseq_col].val.int_val + 1;
@@ -1642,32 +1850,34 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	{
 	{
 		pres_uri.s= (char*)row_vals[pres_uri_col].val.string_val;
 		pres_uri.s= (char*)row_vals[pres_uri_col].val.string_val;
 		pres_uri.len= strlen(pres_uri.s);
 		pres_uri.len= strlen(pres_uri.s);
-		subs->pres_uri.s= (char*)pkg_malloc(pres_uri.len* sizeof(char));
-		if(subs->pres_uri.s== NULL)
-		{	
-			if(subs->reason.s)
-				pkg_free(subs->reason.s);
-			ERR_MEM(PKG_MEM_STR);
+		if(pres_uri.len > 0) {
+			subs->pres_uri.s= (char*)pkg_malloc(pres_uri.len* sizeof(char));
+			if(subs->pres_uri.s== NULL) {
+				if(subs->reason.s)
+					pkg_free(subs->reason.s);
+				ERR_MEM(PKG_MEM_STR);
+			}
+			memcpy(subs->pres_uri.s, pres_uri.s, pres_uri.len);
+			subs->pres_uri.len= pres_uri.len;
 		}
 		}
-		memcpy(subs->pres_uri.s, pres_uri.s, pres_uri.len);
-		subs->pres_uri.len= pres_uri.len;
 	}
 	}
 
 
 	record_route.s= (char*)row_vals[record_route_col].val.string_val;
 	record_route.s= (char*)row_vals[record_route_col].val.string_val;
-	if(record_route.s)
-	{
+	if(record_route.s) {
 		record_route.len= strlen(record_route.s);
 		record_route.len= strlen(record_route.s);
-		subs->record_route.s= (char*)pkg_malloc(record_route.len*sizeof(char));
-		if(subs->record_route.s== NULL)
-		{
-			ERR_MEM(PKG_MEM_STR);
+		if( record_route.len > 0) {
+			subs->record_route.s= (char*)pkg_malloc(record_route.len*sizeof(char));
+			if(subs->record_route.s== NULL) {
+				ERR_MEM(PKG_MEM_STR);
+			}
+			memcpy(subs->record_route.s, record_route.s, record_route.len);
+			subs->record_route.len= record_route.len;
 		}
 		}
-		memcpy(subs->record_route.s, record_route.s, record_route.len);
-		subs->record_route.len= record_route.len;
 	}
 	}
 
 
 	subs->updated= row_vals[updated_col].val.int_val;
 	subs->updated= row_vals[updated_col].val.int_val;
 	subs->updated_winfo= row_vals[updated_winfo_col].val.int_val;
 	subs->updated_winfo= row_vals[updated_winfo_col].val.int_val;
+	subs->flags = row_vals[flags_col].val.int_val;
 
 
 	pa_dbf.free_result(pa_db, result);
 	pa_dbf.free_result(pa_db, result);
 	result= NULL;
 	result= NULL;

+ 2 - 0
modules/presence/subscribe.h

@@ -83,6 +83,8 @@ struct subscription
 	int internal_update_flag;
 	int internal_update_flag;
 	int updated;
 	int updated;
 	int updated_winfo;
 	int updated_winfo;
+	flag_t flags;
+	str user_agent;
 	struct subscription* next;
 	struct subscription* next;
 
 
 };
 };

+ 2 - 2
utils/kamctl/db_berkeley/kamailio/active_watchers

@@ -1,5 +1,5 @@
 METADATA_COLUMNS
 METADATA_COLUMNS
-id(int) presentity_uri(str) watcher_username(str) watcher_domain(str) to_user(str) to_domain(str) event(str) event_id(str) to_tag(str) from_tag(str) callid(str) local_cseq(int) remote_cseq(int) contact(str) record_route(str) expires(int) status(int) reason(str) version(int) socket_info(str) local_contact(str) from_user(str) from_domain(str) updated(int) updated_winfo(int)
+id(int) presentity_uri(str) watcher_username(str) watcher_domain(str) to_user(str) to_domain(str) event(str) event_id(str) to_tag(str) from_tag(str) callid(str) local_cseq(int) remote_cseq(int) contact(str) record_route(str) expires(int) status(int) reason(str) version(int) socket_info(str) local_contact(str) from_user(str) from_domain(str) updated(int) updated_winfo(int) flags(int) user_agent(str)
 METADATA_KEY
 METADATA_KEY
 1 6 
 1 6 
 METADATA_READONLY
 METADATA_READONLY
@@ -7,4 +7,4 @@ METADATA_READONLY
 METADATA_LOGFLAGS
 METADATA_LOGFLAGS
 0
 0
 METADATA_DEFAULTS
 METADATA_DEFAULTS
-NIL|NIL|NIL|NIL|NIL|NIL|'presence'|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|2|NIL|0|NIL|NIL|NIL|NIL|NIL|NIL
+NIL|NIL|NIL|NIL|NIL|NIL|'presence'|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|2|NIL|0|NIL|NIL|NIL|NIL|NIL|NIL|0|''

+ 1 - 1
utils/kamctl/db_berkeley/kamailio/version

@@ -15,7 +15,7 @@ acc|5
 acc_cdrs|
 acc_cdrs|
 acc_cdrs|2
 acc_cdrs|2
 active_watchers|
 active_watchers|
-active_watchers|11
+active_watchers|12
 address|
 address|
 address|6
 address|6
 aliases|
 aliases|

+ 3 - 1
utils/kamctl/db_sqlite/presence-create.sql

@@ -43,6 +43,8 @@ CREATE TABLE active_watchers (
     from_domain VARCHAR(64) NOT NULL,
     from_domain VARCHAR(64) NOT NULL,
     updated INTEGER NOT NULL,
     updated INTEGER NOT NULL,
     updated_winfo INTEGER NOT NULL,
     updated_winfo INTEGER NOT NULL,
+    flags INTEGER DEFAULT 0 NOT NULL,
+    user_agent VARCHAR(255) DEFAULT '' NOT NULL,
     CONSTRAINT active_watchers_active_watchers_idx UNIQUE (callid, to_tag, from_tag)
     CONSTRAINT active_watchers_active_watchers_idx UNIQUE (callid, to_tag, from_tag)
 );
 );
 
 
@@ -51,7 +53,7 @@ CREATE INDEX active_watchers_active_watchers_pres ON active_watchers (presentity
 CREATE INDEX active_watchers_updated_idx ON active_watchers (updated);
 CREATE INDEX active_watchers_updated_idx ON active_watchers (updated);
 CREATE INDEX active_watchers_updated_winfo_idx ON active_watchers (updated_winfo, presentity_uri);
 CREATE INDEX active_watchers_updated_winfo_idx ON active_watchers (updated_winfo, presentity_uri);
 
 
-INSERT INTO version (table_name, table_version) values ('active_watchers','11');
+INSERT INTO version (table_name, table_version) values ('active_watchers','12');
 
 
 CREATE TABLE watchers (
 CREATE TABLE watchers (
     id INTEGER PRIMARY KEY NOT NULL,
     id INTEGER PRIMARY KEY NOT NULL,

+ 1 - 1
utils/kamctl/dbtext/kamailio/active_watchers

@@ -1 +1 @@
-id(int,auto) presentity_uri(string) watcher_username(string) watcher_domain(string) to_user(string) to_domain(string) event(string) event_id(string,null) to_tag(string) from_tag(string) callid(string) local_cseq(int) remote_cseq(int) contact(string) record_route(string,null) expires(int) status(int) reason(string) version(int) socket_info(string) local_contact(string) from_user(string) from_domain(string) updated(int) updated_winfo(int) 
+id(int,auto) presentity_uri(string) watcher_username(string) watcher_domain(string) to_user(string) to_domain(string) event(string) event_id(string,null) to_tag(string) from_tag(string) callid(string) local_cseq(int) remote_cseq(int) contact(string) record_route(string,null) expires(int) status(int) reason(string) version(int) socket_info(string) local_contact(string) from_user(string) from_domain(string) updated(int) updated_winfo(int) flags(int) user_agent(string) 

+ 1 - 1
utils/kamctl/dbtext/kamailio/version

@@ -2,7 +2,7 @@ table_name(string) table_version(int)
 version:1
 version:1
 acc:5
 acc:5
 acc_cdrs:2
 acc_cdrs:2
-active_watchers:11
+active_watchers:12
 address:6
 address:6
 aliases:8
 aliases:8
 carrier_name:1
 carrier_name:1

+ 3 - 1
utils/kamctl/mysql/presence-create.sql

@@ -43,6 +43,8 @@ CREATE TABLE `active_watchers` (
     `from_domain` VARCHAR(64) NOT NULL,
     `from_domain` VARCHAR(64) NOT NULL,
     `updated` INT(11) NOT NULL,
     `updated` INT(11) NOT NULL,
     `updated_winfo` INT(11) NOT NULL,
     `updated_winfo` INT(11) NOT NULL,
+    `flags` INT(11) DEFAULT 0 NOT NULL,
+    `user_agent` VARCHAR(255) DEFAULT '' NOT NULL,
     CONSTRAINT active_watchers_idx UNIQUE (`callid`, `to_tag`, `from_tag`)
     CONSTRAINT active_watchers_idx UNIQUE (`callid`, `to_tag`, `from_tag`)
 );
 );
 
 
@@ -51,7 +53,7 @@ CREATE INDEX active_watchers_pres ON active_watchers (`presentity_uri`, `event`)
 CREATE INDEX updated_idx ON active_watchers (`updated`);
 CREATE INDEX updated_idx ON active_watchers (`updated`);
 CREATE INDEX updated_winfo_idx ON active_watchers (`updated_winfo`, `presentity_uri`);
 CREATE INDEX updated_winfo_idx ON active_watchers (`updated_winfo`, `presentity_uri`);
 
 
-INSERT INTO version (table_name, table_version) values ('active_watchers','11');
+INSERT INTO version (table_name, table_version) values ('active_watchers','12');
 
 
 CREATE TABLE `watchers` (
 CREATE TABLE `watchers` (
     `id` INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
     `id` INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,

+ 3 - 1
utils/kamctl/oracle/presence-create.sql

@@ -51,6 +51,8 @@ CREATE TABLE active_watchers (
     from_domain VARCHAR2(64),
     from_domain VARCHAR2(64),
     updated NUMBER(10),
     updated NUMBER(10),
     updated_winfo NUMBER(10),
     updated_winfo NUMBER(10),
+    flags NUMBER(10) DEFAULT 0 NOT NULL,
+    user_agent VARCHAR2(255) DEFAULT '',
     CONSTRAINT ORA_active_watchers_idx  UNIQUE (callid, to_tag, from_tag)
     CONSTRAINT ORA_active_watchers_idx  UNIQUE (callid, to_tag, from_tag)
 );
 );
 
 
@@ -67,7 +69,7 @@ CREATE INDEX ORA_active_watchers_pres  ON active_watchers (presentity_uri, event
 CREATE INDEX active_watchers_updated_idx  ON active_watchers (updated);
 CREATE INDEX active_watchers_updated_idx  ON active_watchers (updated);
 CREATE INDEX ORA_updated_winfo_idx  ON active_watchers (updated_winfo, presentity_uri);
 CREATE INDEX ORA_updated_winfo_idx  ON active_watchers (updated_winfo, presentity_uri);
 
 
-INSERT INTO version (table_name, table_version) values ('active_watchers','11');
+INSERT INTO version (table_name, table_version) values ('active_watchers','12');
 
 
 CREATE TABLE watchers (
 CREATE TABLE watchers (
     id NUMBER(10) PRIMARY KEY,
     id NUMBER(10) PRIMARY KEY,

+ 3 - 1
utils/kamctl/postgres/presence-create.sql

@@ -43,6 +43,8 @@ CREATE TABLE active_watchers (
     from_domain VARCHAR(64) NOT NULL,
     from_domain VARCHAR(64) NOT NULL,
     updated INTEGER NOT NULL,
     updated INTEGER NOT NULL,
     updated_winfo INTEGER NOT NULL,
     updated_winfo INTEGER NOT NULL,
+    flags INTEGER DEFAULT 0 NOT NULL,
+    user_agent VARCHAR(255) DEFAULT '' NOT NULL,
     CONSTRAINT active_watchers_active_watchers_idx UNIQUE (callid, to_tag, from_tag)
     CONSTRAINT active_watchers_active_watchers_idx UNIQUE (callid, to_tag, from_tag)
 );
 );
 
 
@@ -51,7 +53,7 @@ CREATE INDEX active_watchers_active_watchers_pres ON active_watchers (presentity
 CREATE INDEX active_watchers_updated_idx ON active_watchers (updated);
 CREATE INDEX active_watchers_updated_idx ON active_watchers (updated);
 CREATE INDEX active_watchers_updated_winfo_idx ON active_watchers (updated_winfo, presentity_uri);
 CREATE INDEX active_watchers_updated_winfo_idx ON active_watchers (updated_winfo, presentity_uri);
 
 
-INSERT INTO version (table_name, table_version) values ('active_watchers','11');
+INSERT INTO version (table_name, table_version) values ('active_watchers','12');
 
 
 CREATE TABLE watchers (
 CREATE TABLE watchers (
     id SERIAL PRIMARY KEY NOT NULL,
     id SERIAL PRIMARY KEY NOT NULL,

+ 19 - 11
utils/kamctl/xhttp_pi/pi_framework.xml

@@ -221,6 +221,17 @@
 		<column><field>attrs</field><type>DB1_STR</type></column>
 		<column><field>attrs</field><type>DB1_STR</type></column>
 		<column><field>description</field><type>DB1_STR</type></column>
 		<column><field>description</field><type>DB1_STR</type></column>
 	</db_table>
 	</db_table>
+	<!-- Declaration of domainpolicy table-->
+	<db_table id="domainpolicy">
+		<table_name>domainpolicy</table_name>
+		<db_url_id>mysql</db_url_id>
+		<column><field>id</field><type>DB1_INT</type></column>
+		<column><field>rule</field><type>DB1_STR</type></column>
+		<column><field>type</field><type>DB1_STR</type></column>
+		<column><field>att</field><type>DB1_STR</type></column>
+		<column><field>val</field><type>DB1_STR</type></column>
+		<column><field>description</field><type>DB1_STR</type></column>
+	</db_table>
 	<!-- Declaration of domain table-->
 	<!-- Declaration of domain table-->
 	<db_table id="domain">
 	<db_table id="domain">
 		<table_name>domain</table_name>
 		<table_name>domain</table_name>
@@ -241,17 +252,6 @@
 		<column><field>value</field><type>DB1_STR</type></column>
 		<column><field>value</field><type>DB1_STR</type></column>
 		<column><field>last_modified</field><type>DB1_DATETIME</type></column>
 		<column><field>last_modified</field><type>DB1_DATETIME</type></column>
 	</db_table>
 	</db_table>
-	<!-- Declaration of domainpolicy table-->
-	<db_table id="domainpolicy">
-		<table_name>domainpolicy</table_name>
-		<db_url_id>mysql</db_url_id>
-		<column><field>id</field><type>DB1_INT</type></column>
-		<column><field>rule</field><type>DB1_STR</type></column>
-		<column><field>type</field><type>DB1_STR</type></column>
-		<column><field>att</field><type>DB1_STR</type></column>
-		<column><field>val</field><type>DB1_STR</type></column>
-		<column><field>description</field><type>DB1_STR</type></column>
-	</db_table>
 	<!-- Declaration of dr_gateways table-->
 	<!-- Declaration of dr_gateways table-->
 	<db_table id="dr_gateways">
 	<db_table id="dr_gateways">
 		<table_name>dr_gateways</table_name>
 		<table_name>dr_gateways</table_name>
@@ -536,6 +536,8 @@
 		<column><field>from_domain</field><type>DB1_STR</type></column>
 		<column><field>from_domain</field><type>DB1_STR</type></column>
 		<column><field>updated</field><type>DB1_INT</type></column>
 		<column><field>updated</field><type>DB1_INT</type></column>
 		<column><field>updated_winfo</field><type>DB1_INT</type></column>
 		<column><field>updated_winfo</field><type>DB1_INT</type></column>
+		<column><field>flags</field><type>DB1_INT</type></column>
+		<column><field>user_agent</field><type>DB1_STR</type></column>
 	</db_table>
 	</db_table>
 	<!-- Declaration of watchers table-->
 	<!-- Declaration of watchers table-->
 	<db_table id="watchers">
 	<db_table id="watchers">
@@ -3012,6 +3014,8 @@
 				<col><field>from_domain</field></col>
 				<col><field>from_domain</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated_winfo</field></col>
 				<col><field>updated_winfo</field></col>
+				<col><field>flags</field></col>
+				<col><field>user_agent</field></col>
 			</query_cols>
 			</query_cols>
 		</cmd>
 		</cmd>
 		<cmd><cmd_name>add</cmd_name>
 		<cmd><cmd_name>add</cmd_name>
@@ -3042,6 +3046,8 @@
 				<col><field>from_domain</field></col>
 				<col><field>from_domain</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated_winfo</field></col>
 				<col><field>updated_winfo</field></col>
+				<col><field>flags</field></col>
+				<col><field>user_agent</field></col>
 			</query_cols>
 			</query_cols>
 		</cmd>
 		</cmd>
 		<cmd><cmd_name>update</cmd_name>
 		<cmd><cmd_name>update</cmd_name>
@@ -3075,6 +3081,8 @@
 				<col><field>from_domain</field></col>
 				<col><field>from_domain</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated_winfo</field></col>
 				<col><field>updated_winfo</field></col>
+				<col><field>flags</field></col>
+				<col><field>user_agent</field></col>
 			</query_cols>
 			</query_cols>
 		</cmd>
 		</cmd>
 		<cmd><cmd_name>delete</cmd_name>
 		<cmd><cmd_name>delete</cmd_name>

+ 6 - 0
utils/kamctl/xhttp_pi/presence-mod

@@ -88,6 +88,8 @@
 				<col><field>from_domain</field></col>
 				<col><field>from_domain</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated_winfo</field></col>
 				<col><field>updated_winfo</field></col>
+				<col><field>flags</field></col>
+				<col><field>user_agent</field></col>
 			</query_cols>
 			</query_cols>
 		</cmd>
 		</cmd>
 		<cmd><cmd_name>add</cmd_name>
 		<cmd><cmd_name>add</cmd_name>
@@ -118,6 +120,8 @@
 				<col><field>from_domain</field></col>
 				<col><field>from_domain</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated_winfo</field></col>
 				<col><field>updated_winfo</field></col>
+				<col><field>flags</field></col>
+				<col><field>user_agent</field></col>
 			</query_cols>
 			</query_cols>
 		</cmd>
 		</cmd>
 		<cmd><cmd_name>update</cmd_name>
 		<cmd><cmd_name>update</cmd_name>
@@ -151,6 +155,8 @@
 				<col><field>from_domain</field></col>
 				<col><field>from_domain</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated</field></col>
 				<col><field>updated_winfo</field></col>
 				<col><field>updated_winfo</field></col>
+				<col><field>flags</field></col>
+				<col><field>user_agent</field></col>
 			</query_cols>
 			</query_cols>
 		</cmd>
 		</cmd>
 		<cmd><cmd_name>delete</cmd_name>
 		<cmd><cmd_name>delete</cmd_name>

+ 2 - 0
utils/kamctl/xhttp_pi/presence-table

@@ -42,6 +42,8 @@
 		<column><field>from_domain</field><type>DB1_STR</type></column>
 		<column><field>from_domain</field><type>DB1_STR</type></column>
 		<column><field>updated</field><type>DB1_INT</type></column>
 		<column><field>updated</field><type>DB1_INT</type></column>
 		<column><field>updated_winfo</field><type>DB1_INT</type></column>
 		<column><field>updated_winfo</field><type>DB1_INT</type></column>
+		<column><field>flags</field><type>DB1_INT</type></column>
+		<column><field>user_agent</field><type>DB1_STR</type></column>
 	</db_table>
 	</db_table>
 	<!-- Declaration of watchers table-->
 	<!-- Declaration of watchers table-->
 	<db_table id="watchers">
 	<db_table id="watchers">