Browse Source

modules_k/rls: added DB only mode to rls

- By using DB only mode RLS can be distributed across many Kamailio
  servers for scaling and resilience.
- This change has been in use internally at Crocodile RCS for some time,
  but it is extensive.
- The DB only mode changes have been made in a way that changes the pre-
  existing (hash-table based) mode of operation as little as possible.
  This original mode of operation SHOULD be unaffected by this change.
- This enhancement was made by Paul Pankhurst at Crocodile RCS.
- Also contains a small bug-fix to RLS indices by Hugh Waite.
- I have also exported the RLS API for use in app_lua
pd 14 years ago
parent
commit
7665c7947f

+ 215 - 158
modules_k/rls/README

@@ -1,3 +1,4 @@
+
 Resource List Server
 Resource List Server
 
 
 Anca-Maria Vamanu
 Anca-Maria Vamanu
@@ -8,8 +9,8 @@ Edited by
 
 
 Anca-Maria Vamanu
 Anca-Maria Vamanu
 
 
-   Copyright © 2007 Voice Sistem SRL
-     __________________________________________________________________
+   Copyright © 2007 Voice Sistem SRL
+     _________________________________________________________________
 
 
    Table of Contents
    Table of Contents
 
 
@@ -24,26 +25,29 @@ Anca-Maria Vamanu
         3. Parameters
         3. Parameters
 
 
               3.1. db_url(str)
               3.1. db_url(str)
-              3.2. xcap_table(str)
-              3.3. rlsubs_table(str)
-              3.4. rlpres_table(str)
-              3.5. clean_period (int)
-              3.6. waitn_time (int)
-              3.7. max_expires (int)
-              3.8. hash_size (int)
-              3.9. xcap_root (str)
-              3.10. integrated_xcap_server (int)
-              3.11. to_presence_code (int)
-              3.12. rls_event (str)
-              3.13. outbound_proxy (str)
-              3.14. server_address (str)
-              3.15. max_notify_body_length (int)
+              3.2. xcap_db_url(str)
+              3.3. db_mode(int)
+              3.4. xcap_table(str)
+              3.5. rlsubs_table(str)
+              3.6. rlpres_table(str)
+              3.7. clean_period (int)
+              3.8. waitn_time (int)
+              3.9. max_expires (int)
+              3.10. expires_offset (int)
+              3.11. hash_size (int)
+              3.12. xcap_root (str)
+              3.13. integrated_xcap_server (int)
+              3.14. to_presence_code (int)
+              3.15. rls_event (str)
+              3.16. outbound_proxy (str)
+              3.17. server_address (str)
+              3.18. max_notify_body_length (int)
 
 
         4. Functions
         4. Functions
 
 
-              4.1. rls_handle_subscribe()
-              4.2. rls_handle_notify()
-              4.3. rls_update_subs(uri, event)
+              4.1. rls_handle_subscribe() 
+              4.2. rls_handle_notify() 
+              4.3. rls_update_subs(uri, event) 
 
 
         5. Installation
         5. Installation
 
 
@@ -52,23 +56,26 @@ Anca-Maria Vamanu
    List of Examples
    List of Examples
 
 
    1.1. Set db_url parameter
    1.1. Set db_url parameter
-   1.2. Set xcap_table parameter
-   1.3. Set rlsubs_table parameter
-   1.4. Set rlpres_table parameter
-   1.5. Set clean_period parameter
-   1.6. Set waitn_time parameter
-   1.7. Set max_expires parameter
-   1.8. Set hash_size parameter
-   1.9. Set hash_size parameter
-   1.10. Set integrated_xcap_server parameter
-   1.11. Set to_presence_code parameter
-   1.12. Set rls_event parameter
-   1.13. Set outbound_proxy parameter
-   1.14. Set server_address parameter
-   1.15. Set max_notify_body_length parameter
-   1.16. rls_handle_subscribe usage
-   1.17. rls_handle_notify usage
-   1.18. rls_update_subs usage
+   1.2. Set xcap_db_url parameter
+   1.3. Set db_mode parameter
+   1.4. Set xcap_table parameter
+   1.5. Set rlsubs_table parameter
+   1.6. Set rlpres_table parameter
+   1.7. Set clean_period parameter
+   1.8. Set waitn_time parameter
+   1.9. Set max_expires parameter
+   1.10. Set expires_offset parameter
+   1.11. Set hash_size parameter
+   1.12. Set hash_size parameter
+   1.13. Set integrated_xcap_server parameter
+   1.14. Set to_presence_code parameter
+   1.15. Set rls_event parameter
+   1.16. Set outbound_proxy parameter
+   1.17. Set server_address parameter
+   1.18. Set max_notify_body_length parameter
+   1.19. rls_handle_subscribe usage
+   1.20. rls_handle_notify usage
+   1.21. rls_update_subs usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -83,57 +90,60 @@ Chapter 1. Admin Guide
    3. Parameters
    3. Parameters
 
 
         3.1. db_url(str)
         3.1. db_url(str)
-        3.2. xcap_table(str)
-        3.3. rlsubs_table(str)
-        3.4. rlpres_table(str)
-        3.5. clean_period (int)
-        3.6. waitn_time (int)
-        3.7. max_expires (int)
-        3.8. hash_size (int)
-        3.9. xcap_root (str)
-        3.10. integrated_xcap_server (int)
-        3.11. to_presence_code (int)
-        3.12. rls_event (str)
-        3.13. outbound_proxy (str)
-        3.14. server_address (str)
-        3.15. max_notify_body_length (int)
+        3.2. xcap_db_url(str)
+        3.3. db_mode(int)
+        3.4. xcap_table(str)
+        3.5. rlsubs_table(str)
+        3.6. rlpres_table(str)
+        3.7. clean_period (int)
+        3.8. waitn_time (int)
+        3.9. max_expires (int)
+        3.10. expires_offset (int)
+        3.11. hash_size (int)
+        3.12. xcap_root (str)
+        3.13. integrated_xcap_server (int)
+        3.14. to_presence_code (int)
+        3.15. rls_event (str)
+        3.16. outbound_proxy (str)
+        3.17. server_address (str)
+        3.18. max_notify_body_length (int)
 
 
    4. Functions
    4. Functions
 
 
-        4.1. rls_handle_subscribe()
-        4.2. rls_handle_notify()
-        4.3. rls_update_subs(uri, event)
+        4.1. rls_handle_subscribe() 
+        4.2. rls_handle_notify() 
+        4.3. rls_update_subs(uri, event) 
 
 
    5. Installation
    5. Installation
 
 
 1. Overview
 1. Overview
 
 
-   The modules is a Resource List Server implementation following the
+   The  modules  is  a  Resource List Server implementation following the
    specification in RFC 4662 and RFC 4826.
    specification in RFC 4662 and RFC 4826.
 
 
-   The server is independent from local presence servers, retrieving
+   The  server  is  independent  from  local presence servers, retrieving
    presence information with Subscribe-Notify messages.
    presence information with Subscribe-Notify messages.
 
 
-   The module uses the presence module as a library, as it requires a
-   resembling mechanism for handling Subscribe. Therefore, in case the
-   local presence server is not collocated on the same machine with the RL
-   server, the presence module should be loaded in a library mode only
+   The  module  uses  the  presence module as a library, as it requires a
+   resembling  mechanism  for  handling Subscribe. Therefore, in case the
+   local  presence  server is not collocated on the same machine with the
+   RL server, the presence module should be loaded in a library mode only
    (see doc for presence module).
    (see doc for presence module).
 
 
-   It handles subscription to lists in an event independent way.The
-   default event is presence, but if some other events are to be handled
-   by the server, they should be added using the module parameter
+   It  handles  subscription  to  lists  in  an event independent way.The
+   default  event is presence, but if some other events are to be handled
+   by  the  server,  they  should  be  added  using  the module parameter
    "rls_events".
    "rls_events".
 
 
-   It works with XCAP server for storage. There is also the possibility to
-   configure it to work in an integrated_xcap server mode, when it only
-   queries database for the resource lists documents. This is useful in a
-   small architecture when all the clients use an integrated server and
-   there are no references to exterior documents in their lists.
+   It  works  with XCAP server for storage. There is also the possibility
+   to  configure  it  to  work in an integrated_xcap server mode, when it
+   only queries database for the resource lists documents. This is useful
+   in  a small architecture when all the clients use an integrated server
+   and there are no references to exterior documents in their lists.
 
 
-   The same as presence module, it has a caching mode with periodical
-   update in database for subscribe information. The information retrieved
-   with Notify messages is stored in database only.
+   The  same  as  presence  module, it has a caching mode with periodical
+   update   in   database  for  subscribe  information.  The  information
+   retrieved with Notify messages is stored in database only.
 
 
 2. Dependencies
 2. Dependencies
 
 
@@ -156,219 +166,266 @@ Chapter 1. Admin Guide
 3. Parameters
 3. Parameters
 
 
    3.1. db_url(str)
    3.1. db_url(str)
-   3.2. xcap_table(str)
-   3.3. rlsubs_table(str)
-   3.4. rlpres_table(str)
-   3.5. clean_period (int)
-   3.6. waitn_time (int)
-   3.7. max_expires (int)
-   3.8. hash_size (int)
-   3.9. xcap_root (str)
-   3.10. integrated_xcap_server (int)
-   3.11. to_presence_code (int)
-   3.12. rls_event (str)
-   3.13. outbound_proxy (str)
-   3.14. server_address (str)
-   3.15. max_notify_body_length (int)
+   3.2. xcap_db_url(str)
+   3.3. db_mode(int)
+   3.4. xcap_table(str)
+   3.5. rlsubs_table(str)
+   3.6. rlpres_table(str)
+   3.7. clean_period (int)
+   3.8. waitn_time (int)
+   3.9. max_expires (int)
+   3.10. expires_offset (int)
+   3.11. hash_size (int)
+   3.12. xcap_root (str)
+   3.13. integrated_xcap_server (int)
+   3.14. to_presence_code (int)
+   3.15. rls_event (str)
+   3.16. outbound_proxy (str)
+   3.17. server_address (str)
+   3.18. max_notify_body_length (int)
 
 
 3.1. db_url(str)
 3.1. db_url(str)
 
 
    The database url.
    The database url.
 
 
-   Default value is “mysql://openser:openserrw@localhost/openser�.
+   Default value is "mysql://openser:openserrw@localhost/openser". 
 
 
    Example 1.1. Set db_url parameter
    Example 1.1. Set db_url parameter
 ...
 ...
 modparam("rls", "db_url", "dbdriver://username:password@dbhost/dbname")
 modparam("rls", "db_url", "dbdriver://username:password@dbhost/dbname")
 ...
 ...
 
 
-3.2. xcap_table(str)
+3.2. xcap_db_url(str)
+
+   The  xcap  database  url. This parameter only needs to be specified if
+   the rls db and integerated xcap server db have different urls.
+
+   Default value is a mirror of the "db_url" setting. 
+
+   Example 1.2. Set xcap_db_url parameter
+...
+modparam("rls", "xcap_db_url", "dbdriver://username:password@dbhost/dbname")
+...
+
+3.3. db_mode(int)
+
+   The  module  supports  2  modes  of operation, high speed memory based
+   storage  (mode 0), and database only (mode 2) where all data is stored
+   in a database, allowing scalability at the expense of speed. Mode 1 is
+   reserved.
 
 
-   The name of the xcap table in which the integrated server or the
-   xcap_client module writes. If integrated_xcap_server parameter not set,
-   the name of the table must be the same as the one set for the
+   Default value is "0" 
+
+   Example 1.3. Set db_mode parameter
+...
+modparam("rls", "db_mode", 2)
+...
+
+3.4. xcap_table(str)
+
+   The  name  of  the  xcap  table  in which the integrated server or the
+   xcap_client  module  writes.  If  integrated_xcap_server parameter not
+   set,  the  name  of  the table must be the same as the one set for the
    xcap_client module.
    xcap_client module.
 
 
-   Default value is “xcap�.
+   Default value is "xcap". 
 
 
-   Example 1.2. Set xcap_table parameter
+   Example 1.4. Set xcap_table parameter
 ...
 ...
 modparam("rls", "xcap_table", "xcaps");
 modparam("rls", "xcap_table", "xcaps");
 ...
 ...
 
 
-3.3. rlsubs_table(str)
+3.5. rlsubs_table(str)
 
 
    The name of the db table where resource lists subscription information
    The name of the db table where resource lists subscription information
    is stored.
    is stored.
 
 
-   Default value is “rls_watchers�.
+   Default value is "rls_watchers". 
 
 
-   Example 1.3. Set rlsubs_table parameter
+   Example 1.5. Set rlsubs_table parameter
 ...
 ...
 modparam("rls", "rlsubs_table", "rls_subscriptions")
 modparam("rls", "rlsubs_table", "rls_subscriptions")
 ...
 ...
 
 
-3.4. rlpres_table(str)
+3.6. rlpres_table(str)
 
 
-   The name of the db table where notified event specific information is
+   The  name of the db table where notified event specific information is
    stored.
    stored.
 
 
-   Default value is “rls_presentity�.
+   Default value is "rls_presentity". 
 
 
-   Example 1.4. Set rlpres_table parameter
+   Example 1.6. Set rlpres_table parameter
 ...
 ...
 modparam("rls", "rlpres_table", "rls_notify")
 modparam("rls", "rlpres_table", "rls_notify")
 ...
 ...
 
 
-3.5. clean_period (int)
+3.7. clean_period (int)
 
 
    The period at which to check for expired information.
    The period at which to check for expired information.
 
 
-   Default value is “100�.
+   Default value is "100". 
 
 
-   Example 1.5. Set clean_period parameter
+   Example 1.7. Set clean_period parameter
 ...
 ...
 modparam("rls", "clean_period", 100)
 modparam("rls", "clean_period", 100)
 ...
 ...
 
 
-3.6. waitn_time (int)
+3.8. waitn_time (int)
 
 
-   The timer period at which the server should attempt to send Notifies
-   with the updated presence state of the subscribed list or watcher
+   The  timer  period at which the server should attempt to send Notifies
+   with  the  updated  presence  state  of the subscribed list or watcher
    information.
    information.
 
 
-   Default value is “50�.
+   Default value is "50". 
 
 
-   Example 1.6. Set waitn_time parameter
+   Example 1.8. Set waitn_time parameter
 ...
 ...
 modparam("rls", "waitn_time", 10)
 modparam("rls", "waitn_time", 10)
 ...
 ...
 
 
-3.7. max_expires (int)
+3.9. max_expires (int)
 
 
    The maximum accepted expires for a subscription to a list.
    The maximum accepted expires for a subscription to a list.
 
 
-   Default value is “7200�.
+   Default value is "7200". 
 
 
-   Example 1.7. Set max_expires parameter
+   Example 1.9. Set max_expires parameter
 ...
 ...
 modparam("rls", "max_expires", 10800)
 modparam("rls", "max_expires", 10800)
 ...
 ...
 
 
-3.8. hash_size (int)
+3.10. expires_offset (int)
+
+   This  paramater  only  has an effect when the db_mode is DB_ONLY (mode
+   2).  When  expired  subscribers  are  checked  for  deletion  from the
+   database,  those that have a value in the expires column which is less
+   than  current_time  - expires_offset are matched. Hence when an offset
+   of  zero is used, all those that expire prior the current time will be
+   deleted.  If  an  offset  of 't' is used, only those that expired more
+   than t seconds ago are deleted from the database. Negative offsets are
+   treated as though an offset of zero was specifed.
+
+   Default value is "0". 
+
+   Example 1.10. Set expires_offset parameter
+...
+modparam("rls", "expires_offset", 0)
+...
+
+3.11. hash_size (int)
 
 
-   The dimension of the hash table used to store subscription to a list.
-   This parameter will be used as the power of 2 when computing table
+   The  dimension of the hash table used to store subscription to a list.
+   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.8. Set hash_size parameter
+   Example 1.11. Set hash_size parameter
 ...
 ...
 modparam("rls", "hash_size", 11)
 modparam("rls", "hash_size", 11)
 ...
 ...
 
 
-3.9. xcap_root (str)
+3.12. xcap_root (str)
 
 
    The address of the xcap server.
    The address of the xcap server.
 
 
-   Default value is “NULL�.
+   Default value is "NULL". 
 
 
-   Example 1.9. Set hash_size parameter
+   Example 1.12. Set hash_size parameter
 ...
 ...
 modparam("rls", "xcap_root", "http://192.168.2.132/xcap-root:800")
 modparam("rls", "xcap_root", "http://192.168.2.132/xcap-root:800")
 ...
 ...
 
 
-3.10. integrated_xcap_server (int)
+3.13. integrated_xcap_server (int)
 
 
-   This parameter should be set if only integrated xcap servers are used
+   This  parameter should be set if only integrated xcap servers are used
    to store resource lists.
    to store resource lists.
 
 
-   Default value is “0�.
+   Default value is "0". 
 
 
-   Example 1.10. Set integrated_xcap_server parameter
+   Example 1.13. Set integrated_xcap_server parameter
 ...
 ...
 modparam("rls", "integrated_xcap_server", 1)
 modparam("rls", "integrated_xcap_server", 1)
 ...
 ...
 
 
-3.11. to_presence_code (int)
+3.14. to_presence_code (int)
 
 
-   The code to be returned by rls_handle_subscribe function if the
+   The  code  to  be  returned  by  rls_handle_subscribe  function if the
    processed Subscribe is not a resource list Subscribe. This code can be
    processed Subscribe is not a resource list Subscribe. This code can be
-   used in an architecture with presence and rls servers collocated on the
-   same machine, to call handle_subscribe on the message causing this
+   used  in  an  architecture with presence and rls servers collocated on
+   the same machine, to call handle_subscribe on the message causing this
    code.
    code.
 
 
-   Default value is “0�.
+   Default value is "0". 
 
 
-   Example 1.11. Set to_presence_code parameter
+   Example 1.14. Set to_presence_code parameter
 ...
 ...
 modparam("rls", "to_presence_code", 10)
 modparam("rls", "to_presence_code", 10)
 ...
 ...
 
 
-3.12. rls_event (str)
+3.15. rls_event (str)
 
 
-   The default event that RLS handles is presence. If some other events
-   should also be handled by RLS they should be added using this
+   The  default  event that RLS handles is presence. If some other events
+   should  also  be  handled  by  RLS  they  should  be  added using this
    parameter. It can be set more than once.
    parameter. It can be set more than once.
 
 
-   Default value is “"presence"�.
+   Default value is ""presence"". 
 
 
-   Example 1.12. Set rls_event parameter
+   Example 1.15. Set rls_event parameter
 ...
 ...
 modparam("rls", "rls_event", "dialog;sla")
 modparam("rls", "rls_event", "dialog;sla")
 ...
 ...
 
 
-3.13. outbound_proxy (str)
+3.16. outbound_proxy (str)
 
 
-   The SIP address where to send RLS subscriptions (outbound proxy address
-   as SIP URI).
+   The  SIP  address  where  to  send  RLS  subscriptions (outbound proxy
+   address as SIP URI).
 
 
-   Default value is “NULL�.
+   Default value is "NULL". 
 
 
-   Example 1.13. Set outbound_proxy parameter
+   Example 1.16. Set outbound_proxy parameter
 ...
 ...
 modparam("rls", "outbound_proxy", "sip:presence.kamailio.org")
 modparam("rls", "outbound_proxy", "sip:presence.kamailio.org")
 ...
 ...
 
 
-3.14. server_address (str)
+3.17. server_address (str)
 
 
-   The address of the server that will be used as a contact in sent
-   Subscribe requests and 200 OK replies for Subscribe requests for RLS.
+   The  address  of  the  server  that  will be used as a contact in sent
+   Subscribe  requests and 200 OK replies for Subscribe requests for RLS.
    It is a mandatory parameter.
    It is a mandatory parameter.
 
 
-   Example 1.14. Set server_address parameter
+   Example 1.17. Set server_address parameter
 ...
 ...
 modparam("rls", "server_address", "sip:[email protected]:5060")
 modparam("rls", "server_address", "sip:[email protected]:5060")
 ...
 ...
 
 
-3.15. max_notify_body_length (int)
+3.18. max_notify_body_length (int)
 
 
    The maximum size that the body of a NOTIFY message may be. If set to 0
    The maximum size that the body of a NOTIFY message may be. If set to 0
    (the default), no size limit is applied. Note that this refers only to
    (the default), no size limit is applied. Note that this refers only to
    the body, not the complete NOTIFY message.
    the body, not the complete NOTIFY message.
 
 
-   Example 1.15. Set max_notify_body_length parameter
+   Example 1.18. Set max_notify_body_length parameter
 ...
 ...
 modparam("rls", "max_notify_body_length", 32000)
 modparam("rls", "max_notify_body_length", 32000)
 ...
 ...
 
 
 4. Functions
 4. Functions
 
 
-   4.1. rls_handle_subscribe()
-   4.2. rls_handle_notify()
-   4.3. rls_update_subs(uri, event)
+   4.1. rls_handle_subscribe() 
+   4.2. rls_handle_notify() 
+   4.3. rls_update_subs(uri, event) 
 
 
 4.1.  rls_handle_subscribe()
 4.1.  rls_handle_subscribe()
 
 
    This function detects if a Subscribe message should be handled by RLS.
    This function detects if a Subscribe message should be handled by RLS.
-   If not it replies with the configured to_presence_code. If it is, it
-   extracts the dialog info and sends aggregate Notify requests with
+   If  not  it replies with the configured to_presence_code. If it is, it
+   extracts  the  dialog  info  and  sends aggregate Notify requests with
    information for the list.
    information for the list.
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 
-   Example 1.16. rls_handle_subscribe usage
+   Example 1.19. rls_handle_subscribe usage
 ...
 ...
 For presence and rls on the same machine:
 For presence and rls on the same machine:
         modparam("rls", "to_presence_code", 10)
         modparam("rls", "to_presence_code", 10)
@@ -396,7 +453,7 @@ For rls only:
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 
-   Example 1.17. rls_handle_notify usage
+   Example 1.20. rls_handle_notify usage
 ...
 ...
 if(method=="NOTIFY")
 if(method=="NOTIFY")
     rls_handle_notify();
     rls_handle_notify();
@@ -404,18 +461,18 @@ if(method=="NOTIFY")
 
 
 4.3.  rls_update_subs(uri, event)
 4.3.  rls_update_subs(uri, event)
 
 
-   This function can be used in configuration to trigger updates to
-   resource list subscriptions (for example, after the contents of a
+   This  function  can  be  used  in  configuration to trigger updates to
+   resource  list  subscriptions  (for  example,  after the contents of a
    resource list has changes).
    resource list has changes).
 
 
    Parameters:
    Parameters:
-     * uri - the uri of the user who made the change and whose resource
+     * uri  -  the uri of the user who made the change and whose resource
        list subscriptions should be updated
        list subscriptions should be updated
      * event - the event package (e.g. presence).
      * event - the event package (e.g. presence).
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.18. rls_update_subs usage
+   Example 1.21. rls_update_subs usage
 ...
 ...
 Within event_route[xhttp:request]:
 Within event_route[xhttp:request]:
         case "PUT":
         case "PUT":
@@ -433,10 +490,10 @@ Within event_route[xhttp:request]:
 
 
 5. Installation
 5. Installation
 
 
-   The module requires 2 tables in Kamailio database: rls_presentity and
-   rls_watchers.The SQL syntax to create them can be found in
-   rls-create.sql script in the database directories in the
-   kamailio/scripts folder. You can also find the complete database
+   The  module requires 2 tables in Kamailio database: rls_presentity and
+   rls_watchers.The   SQL   syntax   to  create  them  can  be  found  in
+   rls-create.sql   script   in   the   database   directories   in   the
+   kamailio/scripts  folder.  You  can  also  find  the complete database
    documentation on the project webpage,
    documentation on the project webpage,
    http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html.
    http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html.
 
 

+ 28 - 0
modules_k/rls/api.h

@@ -0,0 +1,28 @@
+#ifndef RLS_API_H
+#define RLS_API_H
+#include "../../str.h"
+
+typedef int (*rls_handle_subscribe_t)(struct sip_msg*, char*, char*);
+typedef int (*rls_handle_notify_t)(struct sip_msg*, char*, char*);
+
+typedef struct rls_binds {
+	rls_handle_subscribe_t rls_handle_subscribe;
+	rls_handle_notify_t rls_handle_notify;
+} rls_api_t;
+
+typedef int (*bind_rls_f)(rls_api_t*);
+
+int bind_rls(struct rls_binds*);
+
+inline static int rls_load_api(rls_api_t *pxb)
+{
+	bind_rls_f bind_rls_exports;
+	if (!(bind_rls_exports = (bind_rls_f)find_export("bind_rls", 1, 0)))
+	{
+		LM_ERR("Failed to import bind_rls\n");
+		return -1;
+	}
+	return bind_rls_exports(pxb);
+}
+
+#endif /*RLS_API_H*/

+ 70 - 0
modules_k/rls/doc/rls_admin.xml

@@ -118,6 +118,50 @@ modparam("rls", "db_url", "&exampledb;")
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>
+
+	<section>
+		<title><varname>xcap_db_url</varname>(str)</title>
+		<para>
+		The xcap database url.
+		This parameter only needs to be specified if the rls db and integerated 
+		xcap server db have different urls. 
+		</para>
+		<para>
+		<emphasis>	Default value is a mirror of the <quote>db_url</quote> setting.	
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xcap_db_url</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rls", "xcap_db_url", "&exampledb;")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>db_mode</varname>(int)</title>
+		<para>
+		The module supports 2 modes of operation, high speed memory
+		based storage (mode 0), and database only (mode 2) where all 
+		data is stored in a database, allowing scalability at the expense of speed.
+		Mode 1 is reserved.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>0</quote> 	
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>db_mode</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rls", "db_mode", 2)
+...
+</programlisting>
+		</example>
+	</section>
+
 	<section>
 	<section>
 		<title><varname>xcap_table</varname>(str)</title>
 		<title><varname>xcap_table</varname>(str)</title>
 		<para>
 		<para>
@@ -233,6 +277,32 @@ modparam("rls", "waitn_time", 10)
 		<programlisting format="linespecific">
 		<programlisting format="linespecific">
 ...
 ...
 modparam("rls", "max_expires", 10800)
 modparam("rls", "max_expires", 10800)
+...
+		</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>expires_offset</varname> (int)</title>
+		<para>
+		This paramater only has an effect when the db_mode is DB_ONLY (mode 2).
+		When expired subscribers are checked for deletion from the database,
+		those that have a value in the expires column which is less than 
+		current_time - expires_offset are matched. Hence when an offset of zero
+		is used, all those that expire prior the current time will be deleted.
+		If an offset of 't' is used, only those that expired more than t seconds
+		ago are deleted from the database. 
+		Negative offsets are treated as though an offset of zero was specifed. 
+		</para>
+		<para>
+		<emphasis>Default value is <quote>0</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>expires_offset</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rls", "expires_offset", 0)
 ...
 ...
 		</programlisting>
 		</programlisting>
 		</example>
 		</example>

+ 56 - 29
modules_k/rls/notify.c

@@ -341,11 +341,23 @@ int agg_body_sendn_update(str* rl_uri, char* boundary_string, str* rlmi_body,
 	pkg_free(body.s);
 	pkg_free(body.s);
 	body.s= NULL;
 	body.s= NULL;
 
 
-	if(pres_update_shtable(rls_table, hash_code,subs, LOCAL_TYPE)< 0)
+	if (dbmode==RLS_DB_ONLY)
 	{
 	{
-		LM_ERR("updating in hash table\n");
-		goto error;
+		if ( update_rlsdb(subs, LOCAL_TYPE) <0 )
+		{
+			LM_ERR( "updating DB\n" );
+			goto error;
+		}
 	}
 	}
+	else
+	{
+		if(pres_update_shtable(rls_table, hash_code,subs, LOCAL_TYPE)< 0)
+		{
+			LM_ERR("updating in hash table\n");
+			goto error;
+		}
+	}
+
 	return 0;
 	return 0;
 
 
 error:
 error:
@@ -884,7 +896,6 @@ void rls_notify_callback( struct cell *t, int type, struct tmcb_params *ps)
 
 
 	if(ps->code >= 300)
 	if(ps->code >= 300)
 	{
 	{
-		/* delete from database table */
 		db_key_t db_keys[2];
 		db_key_t db_keys[2];
 		db_val_t db_vals[2];
 		db_val_t db_vals[2];
 		unsigned int hash_code;
 		unsigned int hash_code;
@@ -896,32 +907,48 @@ void rls_notify_callback( struct cell *t, int type, struct tmcb_params *ps)
 		subs.from_tag= ((dialog_id_t*)(*ps->param))->from_tag;
 		subs.from_tag= ((dialog_id_t*)(*ps->param))->from_tag;
 		subs.callid= ((dialog_id_t*)(*ps->param))->callid;
 		subs.callid= ((dialog_id_t*)(*ps->param))->callid;
 
 
-		if (rls_dbf.use_table(rls_db, &rlsubs_table) < 0) 
+		if (dbmode != RLS_DB_ONLY)
 		{
 		{
-			LM_ERR("in use_table\n");
-			goto done;
-		}
+			/* delete from database table */
+
+
+			if (rls_dbf.use_table(rls_db, &rlsubs_table) < 0) 
+			{
+				LM_ERR("in use_table\n");
+				goto done;
+			}
 		
 		
-		db_keys[0] =&str_to_tag_col;
-		db_vals[0].type = DB1_STR;
-		db_vals[0].nul = 0;
-		db_vals[0].val.str_val = subs.to_tag;
+			db_keys[0] =&str_to_tag_col;
+			db_vals[0].type = DB1_STR;
+			db_vals[0].nul = 0;
+			db_vals[0].val.str_val = subs.to_tag;
 
 
-		db_keys[1] =&str_callid_col;
-		db_vals[1].type = DB1_STR;
-		db_vals[1].nul = 0;
-		db_vals[1].val.str_val = subs.callid;
+			db_keys[1] =&str_callid_col;
+			db_vals[1].type = DB1_STR;
+			db_vals[1].nul = 0;
+			db_vals[1].val.str_val = subs.callid;
 
 
 
 
-		if (rls_dbf.delete(rls_db, db_keys, 0, db_vals, 2) < 0) 
-			LM_ERR("cleaning expired messages\n");	
+			if (rls_dbf.delete(rls_db, db_keys, 0, db_vals, 2) < 0) 
+				LM_ERR("cleaning expired messages\n");	
+		}
 
 
 		/* delete from cache table */
 		/* delete from cache table */
-		hash_code= core_hash(&subs.callid, &subs.to_tag , hash_size);
-
-		if(pres_delete_shtable(rls_table,hash_code, subs.to_tag)< 0)
+		if (dbmode == RLS_DB_ONLY)
 		{
 		{
-			LM_ERR("record not found in hash table\n");
+			if (delete_rlsdb(&subs.callid, &subs.to_tag, NULL) < 0 )
+			{
+				LM_ERR( "unable to delete record from DB\n" );
+			}
+		}
+		else
+		{
+			hash_code= core_hash(&subs.callid, &subs.to_tag , hash_size);
+
+			if(pres_delete_shtable(rls_table,hash_code, subs.to_tag)< 0)
+			{
+				LM_ERR("record not found in hash table\n");
+			}
 		}
 		}
 	}	
 	}	
 
 
@@ -960,7 +987,7 @@ int process_list_and_exec(xmlNodePtr list_node, str username, str domain,
 			{
 			{
 				if (rls_integrated_xcap_server == 1
 				if (rls_integrated_xcap_server == 1
 					&& (hostname.len == 0
 					&& (hostname.len == 0
-						|| check_self(&hostname, port, PROTO_NONE) == 1))
+						|| check_self(&hostname, 0, PROTO_NONE) == 1))
 				{
 				{
 					LM_DBG("fetching local <resource-list/>\n");
 					LM_DBG("fetching local <resource-list/>\n");
 					if (rls_get_resource_list(&rl_uri, &username, &domain, &rl_node, &rl_doc)>0)
 					if (rls_get_resource_list(&rl_uri, &username, &domain, &rl_node, &rl_doc)>0)
@@ -1234,7 +1261,7 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 	query_vals[n_query_cols].val.str_val = root;
 	query_vals[n_query_cols].val.str_val = root;
 	n_query_cols++;
 	n_query_cols++;
 
 
-	if(rls_dbf.use_table(rls_db, &rls_xcap_table) < 0)
+	if(rls_xcap_dbf.use_table(rls_xcap_db, &rls_xcap_table) < 0)
 	{
 	{
 		LM_ERR("in use_table-[table]=%.*s\n",
 		LM_ERR("in use_table-[table]=%.*s\n",
 			rls_xcap_table.len, rls_xcap_table.s);
 			rls_xcap_table.len, rls_xcap_table.s);
@@ -1243,20 +1270,20 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 
 
 	result_cols[xcap_col= n_result_cols++] = &str_doc_col;
 	result_cols[xcap_col= n_result_cols++] = &str_doc_col;
 
 
-	if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols,
+	if(rls_xcap_dbf.query(rls_xcap_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)
 	{
 	{
 		LM_ERR("failed querying table xcap for document: %.*s\n",
 		LM_ERR("failed querying table xcap for document: %.*s\n",
 				root.len, root.s);
 				root.len, root.s);
 		if(result)
 		if(result)
-			rls_dbf.free_result(rls_db, result);
+			rls_xcap_dbf.free_result(rls_xcap_db, result);
 		return -1;
 		return -1;
 	}
 	}
 
 
 	if(result->n<=0)
 	if(result->n<=0)
 	{
 	{
 		LM_DBG("No rl document found\n");
 		LM_DBG("No rl document found\n");
-		rls_dbf.free_result(rls_db, result);
+		rls_xcap_dbf.free_result(rls_xcap_db, result);
 		return -1;
 		return -1;
 	}
 	}
 
 
@@ -1334,12 +1361,12 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		xmlXPathFreeContext(xpathCtx);
 		xmlXPathFreeContext(xpathCtx);
 	}
 	}
 	
 	
-	rls_dbf.free_result(rls_db, result);
+	rls_xcap_dbf.free_result(rls_xcap_db, result);
 	return 1;
 	return 1;
 
 
 error:
 error:
 	if(result!=NULL)
 	if(result!=NULL)
-		rls_dbf.free_result(rls_db, result);
+		rls_xcap_dbf.free_result(rls_xcap_db, result);
 	if(xpathObj!=NULL)
 	if(xpathObj!=NULL)
 		xmlXPathFreeObject(xpathObj);
 		xmlXPathFreeObject(xpathObj);
 	
 	

+ 34 - 12
modules_k/rls/resource_notify.c

@@ -93,25 +93,47 @@ void get_dialog_from_did(char* did, subs_t **dialog, unsigned int *hash_code)
             "resource list Subscribe dialog indentifier(rlsubs did)\n");
             "resource list Subscribe dialog indentifier(rlsubs did)\n");
         return;
         return;
 	}
 	}
-    *hash_code= core_hash(&callid, &to_tag, hash_size);
-    
-    lock_get(&rls_table[*hash_code].lock);
-    s= pres_search_shtable(rls_table,callid,to_tag,from_tag,*hash_code);
-    if(s== NULL)
+
+	if (dbmode == RLS_DB_ONLY)
 	{
 	{
-        LM_ERR("record not found in hash_table [rlsubs_did]= %s\n",
-                did);
-        lock_release(&rls_table[*hash_code].lock);
-        return;
+		*dialog = get_dialog_rlsdb(callid,to_tag,from_tag);
+
+		if(*dialog==NULL)
+		{
+			LM_ERR("record not retrieved from db [rlsubs_did]= %s\n", did);
+			return;
+		}
+	}
+	else
+	{
+		*hash_code= core_hash(&callid, &to_tag, hash_size);
+
+		lock_get(&rls_table[*hash_code].lock);
+		s= pres_search_shtable(rls_table,callid,to_tag,from_tag,*hash_code);
+
+		if(s== NULL)
+		{
+			LM_ERR("record not found in hash_table [rlsubs_did]= %s\n",
+					did);
+			lock_release(&rls_table[*hash_code].lock);
+			return;
+		}
+
+		/* save dialog info */
+		*dialog= pres_copy_subs(s, PKG_MEM_TYPE);
 	}
 	}
 
 
-    /* save dialog info */
-    *dialog= pres_copy_subs(s, PKG_MEM_TYPE);
     if(*dialog== NULL)
     if(*dialog== NULL)
 	{
 	{
         LM_ERR("while copying subs_t structure\n");
         LM_ERR("while copying subs_t structure\n");
 	}
 	}
-    lock_release(&rls_table[*hash_code].lock);
+	else
+	{
+		dump_dialog( *dialog );
+	}
+
+	if (dbmode != RLS_DB_ONLY)
+		lock_release(&rls_table[*hash_code].lock);
 	
 	
 }
 }
 
 

+ 193 - 23
modules_k/rls/rls.c

@@ -54,24 +54,32 @@
 #include "rls.h"
 #include "rls.h"
 #include "notify.h"
 #include "notify.h"
 #include "resource_notify.h"
 #include "resource_notify.h"
+#include "api.h"
 
 
 MODULE_VERSION
 MODULE_VERSION
 
 
 #define P_TABLE_VERSION 1
 #define P_TABLE_VERSION 1
 #define W_TABLE_VERSION 1
 #define W_TABLE_VERSION 1
+#define X_TABLE_VERSION 4
 
 
 /** database connection */
 /** database connection */
 db1_con_t *rls_db = NULL;
 db1_con_t *rls_db = NULL;
 db_func_t rls_dbf;
 db_func_t rls_dbf;
+db1_con_t *rls_xcap_db = NULL;
+db_func_t rls_xcap_dbf;
+db1_con_t *rls2_db = NULL;
+db_func_t rls2_dbf;
 
 
 /** modules variables */
 /** modules variables */
 str rls_server_address = {0, 0};
 str rls_server_address = {0, 0};
+int rls_expires_offset=0;
 int waitn_time = 10;
 int waitn_time = 10;
 str rlsubs_table = str_init("rls_watchers");
 str rlsubs_table = str_init("rls_watchers");
 str rlpres_table = str_init("rls_presentity");
 str rlpres_table = str_init("rls_presentity");
 str rls_xcap_table = str_init("xcap");
 str rls_xcap_table = str_init("xcap");
 
 
 str db_url = str_init(DEFAULT_DB_URL);
 str db_url = str_init(DEFAULT_DB_URL);
+str xcap_db_url = str_init("");
 int hash_size = 512;
 int hash_size = 512;
 shtable_t rls_table;
 shtable_t rls_table;
 int pid;
 int pid;
@@ -93,6 +101,17 @@ xmlNodeGetNodeContentByName_t XMLNodeGetNodeContentByName;
 xmlNodeGetAttrContentByName_t XMLNodeGetAttrContentByName;
 xmlNodeGetAttrContentByName_t XMLNodeGetAttrContentByName;
 
 
 /* functions imported from presence to handle subscribe hash table */
 /* functions imported from presence to handle subscribe hash table */
+extern shtable_t rls_new_shtable(int hash_size);
+extern void rls_destroy_shtable(shtable_t htable, int hash_size);
+extern int rls_insert_shtable(shtable_t htable,unsigned int hash_code, subs_t* subs);
+extern subs_t* rls_search_shtable(shtable_t htable,str callid,str to_tag,
+		str from_tag,unsigned int hash_code);
+extern int rls_delete_shtable(shtable_t htable,unsigned int hash_code,str to_tag);
+extern int rls_update_shtable(shtable_t htable,unsigned int hash_code, 
+		subs_t* subs, int type);
+extern void rls_update_db_subs(db1_con_t *db,db_func_t dbf, shtable_t hash_table,
+	int htable_size, int no_lock, handle_expired_func_t handle_expired_func);
+
 new_shtable_t pres_new_shtable;
 new_shtable_t pres_new_shtable;
 insert_shtable_t pres_insert_shtable;
 insert_shtable_t pres_insert_shtable;
 search_shtable_t pres_search_shtable;
 search_shtable_t pres_search_shtable;
@@ -107,6 +126,7 @@ int to_presence_code = 1;
 int rls_max_expires = 7200;
 int rls_max_expires = 7200;
 int rls_reload_db_subs = 0;
 int rls_reload_db_subs = 0;
 int rls_max_notify_body_len = 0;
 int rls_max_notify_body_len = 0;
+int dbmode = 0;
 
 
 /* functions imported from xcap_client module */
 /* functions imported from xcap_client module */
 xcapGetNewDoc_t xcap_GetNewDoc = 0;
 xcapGetNewDoc_t xcap_GetNewDoc = 0;
@@ -176,12 +196,15 @@ static cmd_export_t cmds[]=
 			0, 0, REQUEST_ROUTE},
 			0, 0, REQUEST_ROUTE},
 	{"rls_update_subs",       (cmd_function)rls_update_subs,	2,
 	{"rls_update_subs",       (cmd_function)rls_update_subs,	2,
 			fixup_update_subs, 0, ANY_ROUTE},
 			fixup_update_subs, 0, ANY_ROUTE},
+	{"bind_rls",              (cmd_function)bind_rls,		1,
+			0, 0, 0},
 	{0, 0, 0, 0, 0, 0 }
 	{0, 0, 0, 0, 0, 0 }
 };
 };
 
 
 static param_export_t params[]={
 static param_export_t params[]={
 	{ "server_address",         STR_PARAM,   &rls_server_address.s           },
 	{ "server_address",         STR_PARAM,   &rls_server_address.s           },
 	{ "db_url",                 STR_PARAM,   &db_url.s                       },
 	{ "db_url",                 STR_PARAM,   &db_url.s                       },
+	{ "xcap_db_url",            STR_PARAM,   &xcap_db_url.s                  },
 	{ "rlsubs_table",           STR_PARAM,   &rlsubs_table.s                 },
 	{ "rlsubs_table",           STR_PARAM,   &rlsubs_table.s                 },
 	{ "rlpres_table",           STR_PARAM,   &rlpres_table.s                 },
 	{ "rlpres_table",           STR_PARAM,   &rlpres_table.s                 },
 	{ "xcap_table",             STR_PARAM,   &rls_xcap_table.s               },
 	{ "xcap_table",             STR_PARAM,   &rls_xcap_table.s               },
@@ -196,6 +219,8 @@ static param_export_t params[]={
 	{ "outbound_proxy",         STR_PARAM,   &rls_outbound_proxy.s           },
 	{ "outbound_proxy",         STR_PARAM,   &rls_outbound_proxy.s           },
 	{ "reload_db_subs",         INT_PARAM,   &rls_reload_db_subs             },
 	{ "reload_db_subs",         INT_PARAM,   &rls_reload_db_subs             },
 	{ "max_notify_body_length", INT_PARAM,	 &rls_max_notify_body_len	     },
 	{ "max_notify_body_length", INT_PARAM,	 &rls_max_notify_body_len	     },
+	{ "db_mode",                INT_PARAM,	 &dbmode			 },
+	{ "expires_offset",         INT_PARAM,	 &rls_expires_offset		 },
 	{0,                         0,           0                               }
 	{0,                         0,           0                               }
 };
 };
 
 
@@ -232,6 +257,12 @@ static int mod_init(void)
 
 
 	LM_DBG("start\n");
 	LM_DBG("start\n");
 
 
+	if (dbmode <RLS_DB_DEFAULT || dbmode > RLS_DB_ONLY)
+	{
+		LM_ERR( "Invalid dbmode-set to default mode\n" );
+		dbmode = 0;
+	}
+
 	if(rls_server_address.s==NULL)
 	if(rls_server_address.s==NULL)
 	{
 	{
 		LM_ERR("server_address parameter not set in configuration file\n");
 		LM_ERR("server_address parameter not set in configuration file\n");
@@ -304,14 +335,35 @@ static int mod_init(void)
 	pres_contains_event = pres.contains_event;
 	pres_contains_event = pres.contains_event;
 	pres_search_event   = pres.search_event;
 	pres_search_event   = pres.search_event;
 	pres_get_ev_list    = pres.get_event_list;
 	pres_get_ev_list    = pres.get_event_list;
-	pres_new_shtable    = pres.new_shtable;
-	pres_destroy_shtable= pres.destroy_shtable;
-	pres_insert_shtable = pres.insert_shtable;
-	pres_delete_shtable = pres.delete_shtable;
-	pres_update_shtable = pres.update_shtable;
-	pres_search_shtable = pres.search_shtable;
+
+	if (rls_expires_offset < 0 ) 
+	{
+		LM_ERR( "Negative expires_offset, defaulted to zero\n" );
+		rls_expires_offset = 0; 
+	}
+
+	if (dbmode == RLS_DB_ONLY)
+	{
+		pres_new_shtable    = rls_new_shtable;
+		pres_destroy_shtable= rls_destroy_shtable;
+		pres_insert_shtable = rls_insert_shtable;
+		pres_delete_shtable = rls_delete_shtable;
+		pres_update_shtable = rls_update_shtable;
+		pres_search_shtable = rls_search_shtable;
+		pres_update_db_subs = rls_update_db_subs;
+	}
+	else
+	{
+		pres_new_shtable    = pres.new_shtable;
+		pres_destroy_shtable= pres.destroy_shtable;
+		pres_insert_shtable = pres.insert_shtable;
+		pres_delete_shtable = pres.delete_shtable;
+		pres_update_shtable = pres.update_shtable;
+		pres_search_shtable = pres.search_shtable;
+		pres_update_db_subs = pres.update_db_subs;
+	}
+
 	pres_copy_subs      = pres.mem_copy_subs;
 	pres_copy_subs      = pres.mem_copy_subs;
-	pres_update_db_subs = pres.update_db_subs;
 	pres_extract_sdialog_info= pres.extract_sdialog_info;
 	pres_extract_sdialog_info= pres.extract_sdialog_info;
 
 
 	if(!pres_contains_event || !pres_get_ev_list || !pres_new_shtable ||
 	if(!pres_contains_event || !pres_get_ev_list || !pres_new_shtable ||
@@ -328,57 +380,112 @@ static int mod_init(void)
 	rls_xcap_table.len= strlen(rls_xcap_table.s);
 	rls_xcap_table.len= strlen(rls_xcap_table.s);
 	db_url.len = db_url.s ? strlen(db_url.s) : 0;
 	db_url.len = db_url.s ? strlen(db_url.s) : 0;
 	LM_DBG("db_url=%s/%d/%p\n", ZSW(db_url.s), db_url.len, db_url.s);
 	LM_DBG("db_url=%s/%d/%p\n", ZSW(db_url.s), db_url.len, db_url.s);
+
+	xcap_db_url.len = xcap_db_url.s ? strlen(xcap_db_url.s) : 0;
+
+	if(xcap_db_url.len==0)
+	{
+		xcap_db_url.s = db_url.s;
+		xcap_db_url.len = db_url.len;
+	}
+
+	LM_DBG("db_url=%s/%d/%p\n", ZSW(xcap_db_url.s), xcap_db_url.len, xcap_db_url.s);
 	
 	
 	/* binding to mysql module  */
 	/* binding to mysql module  */
+
+	/* rls2_db handle is to ensure that there are no unwanted interactions
+	   between the original database reads and the DB_ONLY mode stuff */
+
+	if (db_bind_mod(&db_url, &rls2_dbf))
+	{
+		LM_ERR("Database module not found\n");
+		return -1;
+	}
+
 	if (db_bind_mod(&db_url, &rls_dbf))
 	if (db_bind_mod(&db_url, &rls_dbf))
 	{
 	{
 		LM_ERR("Database module not found\n");
 		LM_ERR("Database module not found\n");
 		return -1;
 		return -1;
 	}
 	}
-	
+
+	if (db_bind_mod(&xcap_db_url, &rls_xcap_dbf))
+	{
+		LM_ERR("Database module not found\n");
+		return -1;
+	}
+
 	if (!DB_CAPABILITY(rls_dbf, DB_CAP_ALL)) {
 	if (!DB_CAPABILITY(rls_dbf, DB_CAP_ALL)) {
 		LM_ERR("Database module does not implement all functions"
 		LM_ERR("Database module does not implement all functions"
 				" needed by the module\n");
 				" needed by the module\n");
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if (!DB_CAPABILITY(rls_xcap_dbf, DB_CAP_ALL)) {
+		LM_ERR("Database module does not implement all functions"
+				" needed by the module\n");
+		return -1;
+	}
+
 	rls_db = rls_dbf.init(&db_url);
 	rls_db = rls_dbf.init(&db_url);
 	if (!rls_db)
 	if (!rls_db)
 	{
 	{
 		LM_ERR("while connecting database\n");
 		LM_ERR("while connecting database\n");
 		return -1;
 		return -1;
 	}
 	}
+
+	rls_xcap_db = rls_xcap_dbf.init(&xcap_db_url);
+	if (!rls_xcap_db)
+	{
+		LM_ERR("while connecting database\n");
+		return -1;
+	}
+
 	/* verify table version */
 	/* verify table version */
 	if((db_check_table_version(&rls_dbf, rls_db, &rlsubs_table, W_TABLE_VERSION) < 0) ||
 	if((db_check_table_version(&rls_dbf, rls_db, &rlsubs_table, W_TABLE_VERSION) < 0) ||
 		(db_check_table_version(&rls_dbf, rls_db, &rlpres_table, P_TABLE_VERSION) < 0)) {
 		(db_check_table_version(&rls_dbf, rls_db, &rlpres_table, P_TABLE_VERSION) < 0)) {
 			LM_ERR("error during table version check.\n");
 			LM_ERR("error during table version check.\n");
 			return -1;
 			return -1;
 	}
 	}
-	
-	if(hash_size<=1)
-		hash_size= 512;
-	else
-		hash_size = 1<<hash_size;
 
 
-	rls_table= pres_new_shtable(hash_size);
-	if(rls_table== NULL)
+	/* verify table version */
+	if(db_check_table_version(&rls_xcap_dbf, rls_xcap_db, &rls_xcap_table, X_TABLE_VERSION) < 0)
 	{
 	{
-		LM_ERR("while creating new hash table\n");
-		return -1;
+			LM_ERR("error during table version check.\n");
+			return -1;
 	}
 	}
-	if(rls_reload_db_subs!=0)
+
+	if (dbmode != RLS_DB_ONLY)
 	{
 	{
-		if(rls_restore_db_subs()< 0)
+		if(hash_size<=1)
+			hash_size= 512;
+		else
+			hash_size = 1<<hash_size;
+
+		rls_table= pres_new_shtable(hash_size);
+		if(rls_table== NULL)
 		{
 		{
-			LM_ERR("while restoring rl watchers table\n");
+			LM_ERR("while creating new hash table\n");
 			return -1;
 			return -1;
 		}
 		}
+		if(rls_reload_db_subs!=0)
+		{
+			if(rls_restore_db_subs()< 0)
+			{
+				LM_ERR("while restoring rl watchers table\n");
+				return -1;
+			}
+		}
 	}
 	}
 
 
 	if(rls_db)
 	if(rls_db)
 		rls_dbf.close(rls_db);
 		rls_dbf.close(rls_db);
 	rls_db = NULL;
 	rls_db = NULL;
 
 
+	if(rls_xcap_db)
+		rls_xcap_dbf.close(rls_xcap_db);
+	rls_xcap_db = NULL;
+
+
 	if(waitn_time<= 0)
 	if(waitn_time<= 0)
 		waitn_time= 5;
 		waitn_time= 5;
 	
 	
@@ -460,10 +567,13 @@ static int mod_init(void)
 	}
 	}
 	register_timer(timer_send_notify,0, waitn_time);
 	register_timer(timer_send_notify,0, waitn_time);
 	
 	
-	register_timer(rls_presentity_clean, 0, clean_period);
-	
-	register_timer(rlsubs_table_update, 0, clean_period);
+	if (clean_period > 0)
+	{
+		register_timer(rls_presentity_clean, 0, clean_period);
 	
 	
+		register_timer(rlsubs_table_update, 0, clean_period);
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -476,6 +586,48 @@ static int child_init(int rank)
 		return 0; /* do nothing for the main process */
 		return 0; /* do nothing for the main process */
 
 
 	LM_DBG("child [%d]  pid [%d]\n", rank, getpid());
 	LM_DBG("child [%d]  pid [%d]\n", rank, getpid());
+
+	if (rls2_dbf.init==0)
+	{
+		LM_CRIT("database not bound\n");
+		return -1;
+	}
+	rls2_db = rls2_dbf.init(&db_url);
+	if (!rls2_db)
+	{
+		LM_ERR("child %d: Error while connecting database\n",
+				rank);
+		return -1;
+	}
+	if (rls2_dbf.use_table(rls2_db, &rlsubs_table) < 0)  
+	{
+		LM_ERR("child %d: Error in use_table rlsubs_table\n", rank);
+		return -1;
+	}
+
+	if (rls_xcap_dbf.init==0)
+	{
+		LM_CRIT("database not bound\n");
+		return -1;
+	}
+
+	rls_xcap_db = rls_xcap_dbf.init(&xcap_db_url);
+	if (!rls_xcap_db)
+	{
+		LM_ERR("child %d: Error while connecting database\n", rank);
+		return -1;
+	}
+	else
+	{
+		if (rls_xcap_dbf.use_table(rls_xcap_db, &rls_xcap_table) < 0)  
+		{
+			LM_ERR("child %d: Error in use_table rls_xcap_table\n", rank);
+			return -1;
+		}
+
+		LM_DBG("child %d: Database connection opened successfully\n", rank);
+	}
+
 	if (rls_dbf.init==0)
 	if (rls_dbf.init==0)
 	{
 	{
 		LM_CRIT("database not bound\n");
 		LM_CRIT("database not bound\n");
@@ -523,6 +675,9 @@ static void destroy(void)
 	}
 	}
 	if(rls_db && rls_dbf.close)
 	if(rls_db && rls_dbf.close)
 		rls_dbf.close(rls_db);
 		rls_dbf.close(rls_db);
+
+	if(rls2_db && rls2_dbf.close)
+		rls2_dbf.close(rls2_db);
 }
 }
 
 
 int handle_expired_record(subs_t* s)
 int handle_expired_record(subs_t* s)
@@ -547,6 +702,8 @@ void rlsubs_table_update(unsigned int ticks,void *param)
 {
 {
 	int no_lock= 0;
 	int no_lock= 0;
 
 
+	if (dbmode==RLS_DB_ONLY) { delete_expired_subs_rlsdb(); return; }
+
 	if(ticks== 0 && param == NULL)
 	if(ticks== 0 && param == NULL)
 		no_lock= 1;
 		no_lock= 1;
 	
 	
@@ -755,3 +912,16 @@ int add_rls_event(modparam_t type, void* val)
 	return 0;
 	return 0;
 
 
 }
 }
+
+int bind_rls(struct rls_binds *pxb)
+{
+		if (pxb == NULL)
+		{
+				LM_WARN("bind_rls: Cannot load rls API into a NULL pointer\n");
+				return -1;
+		}
+
+		pxb->rls_handle_subscribe = rls_handle_subscribe;
+		pxb->rls_handle_notify = rls_handle_notify;
+		return 0;
+}

+ 19 - 1
modules_k/rls/rls.h

@@ -40,6 +40,10 @@
 #include "../../lib/srdb1/db_con.h"
 #include "../../lib/srdb1/db_con.h"
 #include "../../lib/srdb1/db.h"
 #include "../../lib/srdb1/db.h"
 
 
+#define RLS_DB_DEFAULT 0
+#define RLS_DB_RESERVED 1
+#define RLS_DB_ONLY 2
+
 #define NO_UPDATE_TYPE     -1 
 #define NO_UPDATE_TYPE     -1 
 #define UPDATED_TYPE        1 
 #define UPDATED_TYPE        1 
 
 
@@ -79,7 +83,7 @@ typedef struct rls_resource
 	/* the last 2 parameters say if a query in database is needed */
 	/* the last 2 parameters say if a query in database is needed */
 }rls_res_t;
 }rls_res_t;
 
 
-
+extern int dbmode;
 extern char* xcap_root;
 extern char* xcap_root;
 extern unsigned int xcap_port;
 extern unsigned int xcap_port;
 extern str rls_server_address;
 extern str rls_server_address;
@@ -97,10 +101,13 @@ extern int rls_events;
 extern int to_presence_code;
 extern int to_presence_code;
 extern str rls_outbound_proxy;
 extern str rls_outbound_proxy;
 extern int rls_max_notify_body_len;
 extern int rls_max_notify_body_len;
+extern int rls_expires_offset;
 
 
 /* database connection */
 /* database connection */
 extern db1_con_t *rls_db;
 extern db1_con_t *rls_db;
 extern db_func_t rls_dbf;
 extern db_func_t rls_dbf;
+extern db1_con_t *rls_xcap_db;
+extern db_func_t rls_xcap_dbf;
 
 
 extern struct tm_binds tmb;
 extern struct tm_binds tmb;
 extern sl_api_t slb;
 extern sl_api_t slb;
@@ -137,6 +144,17 @@ extern xcap_nodeSel_add_step_t xcap_AddStep;
 extern xcap_nodeSel_add_terminal_t xcap_AddTerminal;
 extern xcap_nodeSel_add_terminal_t xcap_AddTerminal;
 extern xcap_nodeSel_free_t xcap_FreeNodeSel;
 extern xcap_nodeSel_free_t xcap_FreeNodeSel;
 
 
+/* rlsdb functions*/
+int delete_expired_subs_rlsdb( void );
+void dump_dialog( subs_t *s );
+extern int delete_rlsdb( str *callid, str *to_tag, str *from_tag );
+extern int update_rlsdb( subs_t *s, int type );
+extern int update_subs_rlsdb( subs_t *s );
+extern int insert_rlsdb( subs_t *s );
+extern int matches_in_rlsdb( str callid, str to_tag, str from_tag );
+extern int update_all_subs_rlsdb( str *from_user, str *from_domain, str *evt );
+subs_t *get_dialog_rlsdb( str callid, str to_tag, str from_tag );
+
 extern str str_rlsubs_did_col;
 extern str str_rlsubs_did_col;
 extern str str_resource_uri_col;
 extern str str_resource_uri_col;
 extern str str_updated_col;
 extern str str_updated_col;

+ 1220 - 0
modules_k/rls/rls_db.c

@@ -0,0 +1,1220 @@
+/*
+ * $Id$
+ *
+ * rls db - RLS database support 
+ *
+ * Copyright (C) 2011 Crocodile RCS Ltd
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../dprint.h"
+#include "../../lib/srdb1/db.h"
+#include "../../parser/msg_parser.h"
+#include "../../parser/parse_from.h"
+
+#include "rls.h"
+
+#define CONT_COPYDB(buf, dest, source)\
+	do{ \
+	dest.s= (char*)buf+ size;\
+	memcpy(dest.s, source, strlen(source));\
+	dest.len= strlen(source);\
+	size+= strlen(source); \
+	} while(0);
+
+/* database connection */
+extern db1_con_t *rls2_db;
+extern db_func_t rls2_dbf;
+
+extern void update_a_sub(subs_t *subs_copy );
+
+static int rlsdb_debug=0;
+
+/******************************************************************************/
+
+shtable_t rls_new_shtable(int hash_size)
+{
+  LM_ERR( "rls_new_shtable shouldn't be called in RLS_DB_ONLY mode\n" );
+  return(NULL);
+}
+
+/******************************************************************************/
+
+void rls_destroy_shtable(shtable_t htable, int hash_size)
+{
+  LM_ERR( "rls_destroy_shtable shouldn't be called in RLS_DB_ONLY mode\n" );
+}
+
+/******************************************************************************/
+
+int rls_insert_shtable(shtable_t htable,unsigned int hash_code, subs_t* subs)
+{
+  LM_ERR( "rls_insert_shtable shouldn't be called in RLS_DB_ONLY mode\n" );
+  return(-1);
+}
+
+/******************************************************************************/
+
+subs_t* rls_search_shtable(shtable_t htable,str callid,str to_tag,
+		str from_tag,unsigned int hash_code)
+{
+  LM_ERR( "rls_search_shtable shouldn't be called in RLS_DB_ONLY mode\n" );
+  return(NULL);
+}
+
+/******************************************************************************/
+
+int rls_delete_shtable(shtable_t htable,unsigned int hash_code,str to_tag)
+{
+  LM_ERR( "rls_delete_shtable shouldn't be called in RLS_DB_ONLY mode\n" );
+  return(-1);
+}
+
+/******************************************************************************/
+
+int rls_update_shtable(shtable_t htable,unsigned int hash_code, 
+		subs_t* subs, int type)
+{
+  LM_ERR( "rls_update_shtable shouldn't be called in RLS_DB_ONLY mode\n" );
+  return(-1);
+}
+
+/******************************************************************************/
+
+void rls_update_db_subs(db1_con_t *db,db_func_t dbf, shtable_t hash_table,
+	int htable_size, int no_lock, handle_expired_func_t handle_expired_func)
+{
+  LM_ERR( "rls_update_db_subs shouldn't be called in RLS_DB_ONLY mode\n" );
+}
+
+/******************************************************************************/
+/******************************************************************************/
+
+int delete_expired_subs_rlsdb( void )
+
+{
+	db_key_t query_cols[1];
+	db_val_t query_vals[1];
+	db_op_t query_ops[1];
+	int n_query_cols = 0;
+	int expires_col, rval;
+
+	if (rlsdb_debug) LM_ERR( "delete_expired_subs_rlsdb\n" );
+
+	if(rls2_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		return(-1);
+	}
+
+	if(rls2_dbf.use_table(rls2_db, &rlsubs_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		return(-1);
+	}
+	query_cols[expires_col= n_query_cols]= &str_expires_col;
+	query_vals[expires_col].type = DB1_INT;
+	query_vals[expires_col].nul = 0;
+	query_vals[expires_col].val.int_val= (int)time(NULL) - rls_expires_offset;
+	query_ops[expires_col]= OP_LT;
+	n_query_cols++;
+
+	rval=rls2_dbf.delete(rls2_db, query_cols, query_ops, query_vals, n_query_cols);
+
+	if (rval < 0)
+	{
+		LM_ERR( "db delete failed for expired subs\n" );
+	}
+/*	else
+	{
+		LM_ERR( "Done\n" );
+	}*/
+
+	return(rval);
+}
+
+/******************************************************************************/
+
+int delete_rlsdb( str *callid, str *to_tag, str *from_tag ) 
+
+{
+ 	int rval;
+	db_key_t query_cols[3];
+	db_val_t query_vals[3];
+	int n_query_cols = 0;
+	int callid_col, totag_col, fromtag_col;
+
+	if (rlsdb_debug) 
+		LM_ERR( "delete_rlsdb callid=%.*s \nto_tag=%.*s \nfrom_tag=%.*s\n",
+		callid?callid->len:0, callid?callid->s:"",
+		to_tag?to_tag->len:0, to_tag?to_tag->s:"",
+		from_tag?from_tag->len:0, from_tag?from_tag->s:"" ); 
+
+	if(rls2_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		return(-1);
+	}
+
+	if(rls2_dbf.use_table(rls2_db, &rlsubs_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		return(-1);
+	}
+	
+	if (callid)
+	{
+		query_cols[callid_col= n_query_cols] = &str_callid_col;
+		query_vals[callid_col].type = DB1_STR;
+		query_vals[callid_col].nul = 0;
+		query_vals[callid_col].val.str_val= *callid;
+		n_query_cols++;
+	}
+
+	if (to_tag)
+	{
+		query_cols[totag_col= n_query_cols] = &str_to_tag_col;
+		query_vals[totag_col].type = DB1_STR;
+		query_vals[totag_col].nul = 0;
+		query_vals[totag_col].val.str_val= *to_tag;
+		n_query_cols++;
+	}
+
+	if (from_tag)
+	{
+		query_cols[fromtag_col= n_query_cols] = &str_from_tag_col;
+		query_vals[fromtag_col].type = DB1_STR;
+		query_vals[fromtag_col].nul = 0;
+		query_vals[fromtag_col].val.str_val= *from_tag;
+		n_query_cols++;
+	}
+
+	rval = rls2_dbf.delete(rls2_db, query_cols, 0, query_vals, n_query_cols);
+
+	if (rval < 0)
+	{
+		LM_ERR("Can't delete in db\n");
+		return(-1);
+	}
+
+	if (rlsdb_debug) LM_ERR( "done\n" );
+	return(1);
+}
+
+/******************************************************************************/
+
+int update_rlsdb( subs_t *subs, int type)
+
+{
+	db_key_t query_cols[3];
+	db_val_t query_vals[3];
+	db_key_t data_cols[22];
+	db_val_t data_vals[22];
+	int n_query_cols = 0, n_data_cols=0;
+	int callid_col, totag_col, fromtag_col,status_col, event_id_col;
+	int local_cseq_col, remote_cseq_col, expires_col;
+	int contact_col,version_col;
+		
+	
+	if (rlsdb_debug) 
+		LM_ERR( "update_rlsdb callid=%.*s \nto_tag=%.*s \nfrom_tag=%.*s \ntype=%d\n",
+		subs->callid.len, subs->callid.s,
+		subs->to_tag.len, subs->to_tag.s,
+		subs->from_tag.len, subs->from_tag.s, 
+		type );
+
+	if (subs==NULL) return(-1);
+
+	if(rls2_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		return(-1);
+	}
+
+	if(rls2_dbf.use_table(rls2_db, &rlsubs_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		return(-1);
+	}
+	
+	query_cols[callid_col= n_query_cols] = &str_callid_col;
+	query_vals[callid_col].type = DB1_STR;
+	query_vals[callid_col].nul = 0;
+	query_vals[callid_col].val.str_val= subs->callid;
+	n_query_cols++;
+
+	query_cols[totag_col= n_query_cols] = &str_to_tag_col;
+	query_vals[totag_col].type = DB1_STR;
+	query_vals[totag_col].nul = 0;
+	query_vals[totag_col].val.str_val= subs->to_tag;
+	n_query_cols++;
+
+	query_cols[fromtag_col= n_query_cols] = &str_from_tag_col;
+	query_vals[fromtag_col].type = DB1_STR;
+	query_vals[fromtag_col].nul = 0;
+	query_vals[fromtag_col].val.str_val= subs->from_tag;
+	n_query_cols++;
+
+	if(type & REMOTE_TYPE)
+	{
+		data_cols[expires_col= n_data_cols] =&str_expires_col;
+		data_vals[expires_col].type = DB1_INT;
+		data_vals[expires_col].nul = 0;
+		data_vals[expires_col].val.int_val = subs->expires + (int)time(NULL);
+		n_data_cols++;
+
+		data_cols[remote_cseq_col= n_data_cols]=&str_remote_cseq_col;
+		data_vals[remote_cseq_col].type = DB1_INT;
+		data_vals[remote_cseq_col].nul = 0;
+		data_vals[remote_cseq_col].val.int_val= subs->remote_cseq;
+		n_data_cols++;
+	}
+	else
+	{
+		int retrieved_local_cseq=0;
+		db_key_t result_cols[1] = {&str_local_cseq_col};
+		db1_res_t *result= NULL;
+ 		db_val_t *values;
+		db_row_t *rows;
+		int nr_rows;
+
+	
+		if(rls2_dbf.query(rls2_db, query_cols, 0, query_vals, result_cols, 
+				n_query_cols, 1, 0, &result )< 0)
+		{
+			LM_ERR("Can't query db\n");
+			if(result) rls2_dbf.free_result(rls2_db, result);
+			return(-1);
+		}
+
+		if(result == NULL) return(-1);
+
+		nr_rows = RES_ROW_N(result);
+
+		if (nr_rows == 0)
+		{
+			/* no match */ 
+			LM_ERR( "update_rlsdb: NO MATCH\n" );
+			rls2_dbf.free_result(rls2_db, result);
+			return(-1);
+		}
+
+		if (nr_rows != 1)
+		{
+			LM_ERR( "update_rlsdb: TOO MANY MATCHES=%d\n", nr_rows);
+			rls2_dbf.free_result(rls2_db, result);
+			return(-1);
+		}
+
+
+		/* get the results and fill in return data structure */
+		rows = RES_ROWS(result);
+		values = ROW_VALUES(rows);
+
+		retrieved_local_cseq = VAL_INT(values);
+		rls2_dbf.free_result(rls2_db, result);
+
+		subs->local_cseq = ++retrieved_local_cseq;
+		data_cols[local_cseq_col= n_data_cols]=&str_local_cseq_col;
+		data_vals[local_cseq_col].type = DB1_INT;
+		data_vals[local_cseq_col].nul = 0;
+		data_vals[local_cseq_col].val.int_val= subs->local_cseq;
+		n_data_cols++;
+
+		subs->version++;
+		data_cols[version_col= n_data_cols]=&str_version_col;
+		data_vals[version_col].type = DB1_INT;
+		data_vals[version_col].nul = 0;
+		data_vals[version_col].val.int_val= subs->version;
+		n_data_cols++;
+	}
+	
+
+	data_cols[contact_col= n_data_cols] =&str_contact_col;
+	data_vals[contact_col].type = DB1_STR;
+	data_vals[contact_col].nul = 0;
+	data_vals[contact_col].val.str_val = subs->contact;
+	n_data_cols++;
+
+	data_cols[status_col= n_data_cols] =&str_status_col;
+	data_vals[status_col].type = DB1_INT;
+	data_vals[status_col].nul = 0;
+	data_vals[status_col].val.int_val= subs->status;
+	n_data_cols++;
+
+	data_cols[event_id_col= n_data_cols] = &str_event_id_col;
+	data_vals[event_id_col].type = DB1_STR;
+	data_vals[event_id_col].nul = 0;
+	data_vals[event_id_col].val.str_val = subs->event_id;
+	n_data_cols++;
+
+	/*if(s->db_flag & NO_UPDATEDB_FLAG)
+		s->db_flag= UPDATEDB_FLAG; Note this is a retieve & modify of the flag
+
+	data_cols[db_flag_col= n_data_cols]=&str_db_flag_col;
+	data_vals[db_flag_col].type = DB1_INT;
+	data_vals[db_flag_col].nul = 0;
+	data_vals[db_flag_col].val.int_val= subs->db_flag;
+	n_data_cols++;*/
+
+	if(rls2_dbf.update(rls2_db, query_cols, 0, query_vals,
+                    data_cols,data_vals,n_query_cols,n_data_cols) < 0)
+	{
+		LM_ERR("Failed update db\n");
+		return(-1);
+	}
+
+	if (rlsdb_debug) LM_ERR("Done\n");
+	return(0);
+}
+
+/******************************************************************************/
+
+int update_all_subs_rlsdb( str *from_user, str *from_domain, str *evt )
+
+{
+	db_key_t query_cols[3];
+	db_val_t query_vals[3];
+	db_key_t result_cols[22];
+	int n_query_cols = 0, n_result_cols=0;
+	int callid_col, totag_col, fromtag_col;	
+	int r_pres_uri_col,r_to_user_col,r_to_domain_col;
+	int r_from_user_col,r_from_domain_col,r_callid_col;
+	int r_to_tag_col,r_from_tag_col,r_socket_info_col;
+	int r_event_id_col,r_local_contact_col,r_contact_col;
+	int r_record_route_col, r_reason_col;
+	int r_event_col, r_local_cseq_col, r_remote_cseq_col;
+	int r_status_col, r_version_col;
+	int r_expires_col;
+	db1_res_t *result= NULL;
+ 	db_val_t *values;
+	db_row_t *rows;
+	int nr_rows, size, loop;
+	subs_t *dest;
+	event_t parsed_event;
+	str ev_sname;
+
+
+	if (rlsdb_debug)
+		LM_ERR( "update_all_subs_rlsdb from_user=%.*s \nfrom_domain=%.*s evt=%.*s\n",
+		from_user?from_user->len:0, from_user?from_user->s:"",
+		from_domain?from_domain->len:0, from_domain?from_domain->s:"",
+		evt?evt->len:0, evt?evt->s:"" );
+
+	if(rls2_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		return(-1);
+	}
+
+	if(rls2_dbf.use_table(rls2_db, &rlsubs_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		return(-1);
+	}
+	
+	query_cols[callid_col= n_query_cols] = &str_watcher_username_col;
+	query_vals[callid_col].type = DB1_STR;
+	query_vals[callid_col].nul = 0;
+	query_vals[callid_col].val.str_val= *from_user;
+	n_query_cols++;
+
+	query_cols[totag_col= n_query_cols] = &str_watcher_domain_col;
+	query_vals[totag_col].type = DB1_STR;
+	query_vals[totag_col].nul = 0;
+	query_vals[totag_col].val.str_val= *from_domain;
+	n_query_cols++;
+
+	query_cols[fromtag_col= n_query_cols] = &str_event_col;
+	query_vals[fromtag_col].type = DB1_STR;
+	query_vals[fromtag_col].nul = 0;
+	query_vals[fromtag_col].val.str_val= *evt;
+	n_query_cols++;
+
+	result_cols[r_pres_uri_col=n_result_cols++] = &str_presentity_uri_col;
+	result_cols[r_to_user_col=n_result_cols++] = &str_to_user_col;
+	result_cols[r_to_domain_col=n_result_cols++] = &str_to_domain_col;
+	result_cols[r_from_user_col=n_result_cols++] = &str_watcher_username_col;
+	result_cols[r_from_domain_col=n_result_cols++] = &str_watcher_domain_col;
+	result_cols[r_callid_col=n_result_cols++] = &str_callid_col;
+	result_cols[r_to_tag_col=n_result_cols++] = &str_to_tag_col;
+	result_cols[r_from_tag_col=n_result_cols++] = &str_from_tag_col;
+	result_cols[r_socket_info_col=n_result_cols++] = &str_socket_info_col;
+	result_cols[r_event_id_col=n_result_cols++] = &str_event_id_col;
+	result_cols[r_local_contact_col=n_result_cols++] = &str_local_contact_col;
+	result_cols[r_contact_col=n_result_cols++] = &str_contact_col;
+	result_cols[r_record_route_col=n_result_cols++] = &str_record_route_col;
+	result_cols[r_reason_col=n_result_cols++] = &str_reason_col;
+	result_cols[r_event_col=n_result_cols++] = &str_event_col;
+	result_cols[r_local_cseq_col=n_result_cols++] = &str_local_cseq_col;
+	result_cols[r_remote_cseq_col=n_result_cols++] = &str_remote_cseq_col;
+	result_cols[r_status_col=n_result_cols++] = &str_status_col;
+	result_cols[r_version_col=n_result_cols++] = &str_version_col;
+	/*result_cols[r_send_on_cback_col=n_result_cols++] = &str_send_on_cback_col;*/
+	result_cols[r_expires_col=n_result_cols++] = &str_expires_col;
+
+
+	if(rls2_dbf.query(rls2_db, query_cols, 0, query_vals, result_cols, 
+				n_query_cols, n_result_cols, 0, &result )< 0)
+	{
+		LM_ERR("Can't query db\n");
+		if(result) rls2_dbf.free_result(rls2_db, result);
+		return(-1);
+	}
+
+	if(result == NULL) return(-1);
+
+	nr_rows = RES_ROW_N(result);
+
+	if (nr_rows == 0)
+	{
+		/* no match */ 
+		LM_ERR( "update_all_subs_rlsdb: NO MATCH\n" );
+		rls2_dbf.free_result(rls2_db, result);
+		return(-1);
+	}
+
+	if (nr_rows != 1)
+	{
+		LM_ERR( "update_all_subs_rlsdb: TOO MANY MATCHES=%d\n", nr_rows);
+		rls2_dbf.free_result(rls2_db, result);
+		return(-1);
+	}
+
+	/* get the results and fill in return data structure */
+	for (loop=0; loop <nr_rows; loop++)
+	{
+		rows = RES_ROWS(result);
+		values = ROW_VALUES(rows);
+
+		size= sizeof(subs_t) +
+			( strlen(VAL_STRING(values+r_pres_uri_col))
+			+ strlen(VAL_STRING(values+r_to_user_col))
+			+ strlen(VAL_STRING(values+r_to_domain_col))
+			+ strlen(VAL_STRING(values+r_from_user_col))
+			+ strlen(VAL_STRING(values+r_from_domain_col))
+			+ strlen(VAL_STRING(values+r_to_tag_col))
+			+ strlen(VAL_STRING(values+r_from_tag_col))
+			+ strlen(VAL_STRING(values+r_callid_col))
+			+ strlen(VAL_STRING(values+r_socket_info_col))
+			+ strlen(VAL_STRING(values+r_local_contact_col))
+			+ strlen(VAL_STRING(values+r_contact_col))
+			+ strlen(VAL_STRING(values+r_record_route_col))
+			+ strlen(VAL_STRING(values+r_event_id_col))
+			+ strlen(VAL_STRING(values+r_reason_col))+ 1)*sizeof(char);
+
+		dest= (subs_t*)pkg_malloc(size);
+	
+		if(dest== NULL)
+		{
+			LM_ERR( "Can't allocate memory\n" );
+			/* tidy up and return >>>>>>>>>>>>>>>>>>>> */
+			rls2_dbf.free_result(rls2_db, result);
+			return(-1);
+		}
+		memset(dest, 0, size);
+		size= sizeof(subs_t);
+
+		CONT_COPYDB(dest, dest->pres_uri, VAL_STRING(values+r_pres_uri_col))
+		CONT_COPYDB(dest, dest->to_user, VAL_STRING(values+r_to_user_col))
+		CONT_COPYDB(dest, dest->to_domain, VAL_STRING(values+r_to_domain_col))
+		CONT_COPYDB(dest, dest->from_user, VAL_STRING(values+r_from_user_col))
+		CONT_COPYDB(dest, dest->from_domain, VAL_STRING(values+r_from_domain_col))
+		CONT_COPYDB(dest, dest->to_tag, VAL_STRING(values+r_to_tag_col))
+		CONT_COPYDB(dest, dest->from_tag, VAL_STRING(values+r_from_tag_col))
+		CONT_COPYDB(dest, dest->callid, VAL_STRING(values+r_callid_col))
+		CONT_COPYDB(dest, dest->sockinfo_str, VAL_STRING(values+r_socket_info_col))
+		CONT_COPYDB(dest, dest->local_contact, VAL_STRING(values+r_local_contact_col))
+		CONT_COPYDB(dest, dest->contact, VAL_STRING(values+r_contact_col))
+		CONT_COPYDB(dest, dest->record_route, VAL_STRING(values+r_record_route_col))
+
+		if(strlen(VAL_STRING(values+r_event_id_col)) > 0)
+			CONT_COPYDB(dest, dest->event_id, VAL_STRING(values+r_event_id_col))
+
+		if(strlen(VAL_STRING(values+r_reason_col)) > 0)
+			CONT_COPYDB(dest, dest->reason, VAL_STRING(values+r_reason_col))
+
+
+		ev_sname.s= (char *)VAL_STRING(values+r_event_col);
+		ev_sname.len= strlen(ev_sname.s);
+		
+		dest->event = pres_contains_event(&ev_sname, &parsed_event);
+
+		if(dest->event == NULL)
+		{
+			LM_ERR("event not found and set to NULL\n");
+		}
+
+		dest->local_cseq= VAL_INT(values+r_local_cseq_col);
+		dest->remote_cseq= VAL_INT(values+r_remote_cseq_col);
+		dest->status= VAL_INT(values+r_status_col);
+		dest->version= VAL_INT(values+r_version_col);
+		/*dest->send_on_cback= VAL_INT(values+r_send_on_cback_col);*/
+		dest->expires= VAL_INT(values+r_expires_col);
+
+		/*dest->db_flag= VAL_INT(values+r_db_flag_col);*/
+		
+		update_a_sub(dest);
+	}
+
+	rls2_dbf.free_result(rls2_db, result);
+	if (rlsdb_debug) LM_ERR( "Done\n" );
+	return(1);	
+}
+
+/******************************************************************************/
+
+int update_subs_rlsdb( subs_t *subs )
+
+{
+	db_key_t query_cols[3];
+	db_val_t query_vals[3];
+	db_key_t data_cols[22];
+	db_val_t data_vals[22];
+	db_key_t result_cols[10];
+	int n_query_cols = 0, n_data_cols=0, n_result_cols=0;
+	int callid_col, totag_col, fromtag_col;
+	int local_cseq_col, expires_col;	
+	int r_remote_cseq=0, r_local_cseq=0, r_version=0;
+	char *r_record_route, *r_pres_uri;
+	db1_res_t *result= NULL;
+ 	db_val_t *values;
+	db_row_t *rows;
+	int nr_rows;
+
+	if (rlsdb_debug) LM_ERR( "update_subs_rlsdb\n" );
+
+	if (subs==NULL) return(-1);
+
+	if(rls2_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		return(-1);
+	}
+
+	if(rls2_dbf.use_table(rls2_db, &rlsubs_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		return(-1);
+	}
+	
+	query_cols[callid_col= n_query_cols] = &str_callid_col;
+	query_vals[callid_col].type = DB1_STR;
+	query_vals[callid_col].nul = 0;
+	query_vals[callid_col].val.str_val= subs->callid;
+	n_query_cols++;
+
+	query_cols[totag_col= n_query_cols] = &str_to_tag_col;
+	query_vals[totag_col].type = DB1_STR;
+	query_vals[totag_col].nul = 0;
+	query_vals[totag_col].val.str_val= subs->to_tag;
+	n_query_cols++;
+
+	query_cols[fromtag_col= n_query_cols] = &str_from_tag_col;
+	query_vals[fromtag_col].type = DB1_STR;
+	query_vals[fromtag_col].nul = 0;
+	query_vals[fromtag_col].val.str_val= subs->from_tag;
+	n_query_cols++;
+
+	result_cols[n_result_cols++] = &str_remote_cseq_col;
+	result_cols[n_result_cols++] = &str_local_cseq_col;
+	result_cols[n_result_cols++] = &str_version_col;
+	result_cols[n_result_cols++] = &str_presentity_uri_col;
+	result_cols[n_result_cols++] = &str_record_route_col;
+
+	if(rls2_dbf.query(rls2_db, query_cols, 0, query_vals, result_cols, 
+				n_query_cols, n_result_cols, 0, &result )< 0)
+	{
+		LM_ERR("Can't query db\n");
+		if(result) rls2_dbf.free_result(rls2_db, result);
+		return(-1);
+	}
+
+	if(result == NULL) return(-1);
+
+	nr_rows = RES_ROW_N(result);
+
+	if (nr_rows == 0)
+	{
+		/* no match */ 
+		LM_ERR( "update_subs_rlsdb: NO MATCH\n" );
+		rls2_dbf.free_result(rls2_db, result);
+		return(-1);
+	}
+
+	if (nr_rows != 1)
+	{
+		LM_ERR( "update_subs_rlsdb: TOO MANY MATCHES=%d\n", nr_rows);
+		rls2_dbf.free_result(rls2_db, result);
+		return(-1);
+	}
+
+	/* get the results and fill in return data structure */
+	rows = RES_ROWS(result);
+	values = ROW_VALUES(rows);
+
+	r_remote_cseq = VAL_INT(values+0);
+	r_local_cseq = VAL_INT(values+1);
+	r_version = VAL_INT(values+2);
+	r_pres_uri = (char *)VAL_STRING(values+3);
+	r_record_route = (char *)VAL_STRING(values+4);
+	
+	data_cols[expires_col= n_data_cols] = &str_expires_col;
+	data_vals[expires_col].type = DB1_INT;
+	data_vals[expires_col].nul = 0;
+	data_vals[expires_col].val.int_val = subs->expires + (int)time(NULL);
+	n_data_cols++;
+
+	if ( r_remote_cseq >= subs->remote_cseq)
+	{
+		LM_DBG("stored cseq= %d\n", r_remote_cseq);
+		return(401); /*stale cseq code */
+	}
+
+	data_cols[local_cseq_col= n_data_cols] = &str_remote_cseq_col;
+	data_vals[local_cseq_col].type = DB1_INT;
+	data_vals[local_cseq_col].nul = 0;
+	data_vals[local_cseq_col].val.int_val= subs->remote_cseq;
+	n_data_cols++;
+
+	subs->pres_uri.s = (char*)pkg_malloc(strlen(r_pres_uri) * sizeof(char));
+	if(subs->pres_uri.s== NULL)
+	{
+		LM_ERR( "Out of Memory\n" );
+		rls2_dbf.free_result(rls2_db, result);
+		return(-1);
+	}
+
+	memcpy(subs->pres_uri.s, r_pres_uri, strlen(r_pres_uri));
+	subs->pres_uri.len= strlen(r_pres_uri);
+
+	if(strlen(r_record_route) > 0)
+	{
+		subs->record_route.s =
+				(char*)pkg_malloc(strlen(r_record_route) * sizeof(char));
+		if(subs->record_route.s==NULL)
+		{
+			LM_ERR( "Out of Memory\n" );
+ 			pkg_free(subs->pres_uri.s);
+			rls2_dbf.free_result(rls2_db, result);
+			return(-1);
+		}
+		memcpy(subs->record_route.s, 
+			r_record_route, strlen(r_record_route));
+		subs->record_route.len= strlen(r_record_route);
+	}
+
+	subs->local_cseq= r_local_cseq;
+	subs->version= r_version;
+
+	rls2_dbf.free_result(rls2_db, result);
+
+
+	/*if(s->db_flag & NO_UPDATEDB_FLAG)
+		s->db_flag= UPDATEDB_FLAG; Note this is a retieve & modify of the flag
+
+	data_cols[db_flag_col= n_data_cols]=&str_db_flag_col;
+	data_vals[db_flag_col].type = DB1_INT;
+	data_vals[db_flag_col].nul = 0;
+	data_vals[db_flag_col].val.int_val= s->db_flag;
+	n_data_cols++;*/
+
+	if(rls2_dbf.update(rls2_db, query_cols, 0, query_vals,
+                    data_cols,data_vals,n_query_cols,n_data_cols) < 0)
+	{
+		LM_ERR("Failed update db\n");
+		return(-1);
+	}
+	
+	if (rlsdb_debug) LM_ERR("Done\n" );
+	return(0);
+}
+
+/******************************************************************************/
+
+int insert_rlsdb( subs_t *s )
+
+{
+	db_key_t data_cols[22];
+	db_val_t data_vals[22];
+	int n_data_cols = 0;
+	int pres_uri_col, to_user_col, to_domain_col, from_user_col, from_domain_col;
+	int callid_col, totag_col, fromtag_col, event_col,status_col, event_id_col;
+	int local_cseq_col, remote_cseq_col, expires_col, record_route_col;
+	int contact_col, local_contact_col, version_col,socket_info_col,reason_col;
+		
+	if (rlsdb_debug)
+		LM_ERR( "insert_rlsdb callid=%.*s \nto_tag=%.*s \nfrom_tag=%.*s\n",
+		s->callid.len, s->callid.s,
+		s->to_tag.len, s->to_tag.s,
+		s->from_tag.len, s->from_tag.s );
+
+	if (s==NULL) return(-1);
+
+	if(rls2_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		return(-1);
+	}
+
+	if(rls2_dbf.use_table(rls2_db, &rlsubs_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		return(-1);
+	}
+	
+	data_cols[pres_uri_col= n_data_cols] = &str_presentity_uri_col;
+	data_vals[pres_uri_col].type = DB1_STR;
+	data_vals[pres_uri_col].nul = 0;
+	data_vals[pres_uri_col].val.str_val= s->pres_uri;
+	n_data_cols++;
+	
+	data_cols[callid_col= n_data_cols] = &str_callid_col;
+	data_vals[callid_col].type = DB1_STR;
+	data_vals[callid_col].nul = 0;
+	data_vals[callid_col].val.str_val= s->callid;
+	n_data_cols++;
+
+	data_cols[totag_col= n_data_cols] = &str_to_tag_col;
+	data_vals[totag_col].type = DB1_STR;
+	data_vals[totag_col].nul = 0;
+	data_vals[totag_col].val.str_val= s->to_tag;
+	n_data_cols++;
+
+	data_cols[fromtag_col= n_data_cols] = &str_from_tag_col;
+	data_vals[fromtag_col].type = DB1_STR;
+	data_vals[fromtag_col].nul = 0;
+	data_vals[fromtag_col].val.str_val= s->from_tag;
+	n_data_cols++;
+
+	data_cols[to_user_col= n_data_cols] = &str_to_user_col;
+	data_vals[to_user_col].type = DB1_STR;
+	data_vals[to_user_col].nul = 0;
+	data_vals[to_user_col].val.str_val = s->to_user;
+	n_data_cols++;
+
+	data_cols[to_domain_col= n_data_cols] = &str_to_domain_col;
+	data_vals[to_domain_col].type = DB1_STR;
+	data_vals[to_domain_col].nul = 0;
+	data_vals[to_domain_col].val.str_val = s->to_domain;
+	n_data_cols++;
+	
+	data_cols[from_user_col= n_data_cols] = &str_watcher_username_col;
+	data_vals[from_user_col].type = DB1_STR;
+	data_vals[from_user_col].nul = 0;
+	data_vals[from_user_col].val.str_val = s->from_user;
+	n_data_cols++;
+
+	data_cols[from_domain_col= n_data_cols] = &str_watcher_domain_col;
+	data_vals[from_domain_col].type = DB1_STR;
+	data_vals[from_domain_col].nul = 0;
+	data_vals[from_domain_col].val.str_val = s->from_domain;
+	n_data_cols++;
+
+	data_cols[event_col= n_data_cols] = &str_event_col;
+	data_vals[event_col].type = DB1_STR;
+	data_vals[event_col].nul = 0;
+	data_vals[event_col].val.str_val = s->event->name;
+	n_data_cols++;	
+
+	data_cols[event_id_col= n_data_cols] = &str_event_id_col;
+	data_vals[event_id_col].type = DB1_STR;
+	data_vals[event_id_col].nul = 0;
+	data_vals[event_id_col].val.str_val = s->event_id;
+	n_data_cols++;
+
+	data_cols[local_cseq_col= n_data_cols]=&str_local_cseq_col;
+	data_vals[local_cseq_col].type = DB1_INT;
+	data_vals[local_cseq_col].nul = 0;
+	data_vals[local_cseq_col].val.int_val= s->local_cseq;
+	n_data_cols++;
+
+	data_cols[remote_cseq_col= n_data_cols]=&str_remote_cseq_col;
+	data_vals[remote_cseq_col].type = DB1_INT;
+	data_vals[remote_cseq_col].nul = 0;
+	data_vals[remote_cseq_col].val.int_val= s->remote_cseq;
+	n_data_cols++;
+
+	data_cols[expires_col= n_data_cols] =&str_expires_col;
+	data_vals[expires_col].type = DB1_INT;
+	data_vals[expires_col].nul = 0;
+	data_vals[expires_col].val.int_val = s->expires + (int)time(NULL);
+	n_data_cols++;
+
+	data_cols[status_col= n_data_cols] =&str_status_col;
+	data_vals[status_col].type = DB1_INT;
+	data_vals[status_col].nul = 0;
+	data_vals[status_col].val.int_val= s->status;
+	n_data_cols++;
+
+	data_cols[reason_col= n_data_cols] =&str_reason_col;
+	data_vals[reason_col].type = DB1_STR;
+	data_vals[reason_col].nul = 0;
+	data_vals[reason_col].val.str_val= s->reason;
+	n_data_cols++;
+
+	data_cols[record_route_col= n_data_cols] =&str_record_route_col;
+	data_vals[record_route_col].type = DB1_STR;
+	data_vals[record_route_col].nul = 0;
+	data_vals[record_route_col].val.str_val = s->record_route;
+	n_data_cols++;
+	
+	data_cols[contact_col= n_data_cols] =&str_contact_col;
+	data_vals[contact_col].type = DB1_STR;
+	data_vals[contact_col].nul = 0;
+	data_vals[contact_col].val.str_val = s->contact;
+	n_data_cols++;
+
+	data_cols[local_contact_col= n_data_cols] =&str_local_contact_col;
+	data_vals[local_contact_col].type = DB1_STR;
+	data_vals[local_contact_col].nul = 0;
+	data_vals[local_contact_col].val.str_val = s->local_contact;
+	n_data_cols++;
+
+	data_cols[socket_info_col= n_data_cols] =&str_socket_info_col;
+	data_vals[socket_info_col].type = DB1_STR;
+	data_vals[socket_info_col].nul = 0;
+	data_vals[socket_info_col].val.str_val= s->sockinfo_str;
+	n_data_cols++;
+
+	data_cols[version_col= n_data_cols]=&str_version_col;
+	data_vals[version_col].type = DB1_INT;
+	data_vals[version_col].nul = 0;
+	data_vals[version_col].val.int_val= s->version;
+	n_data_cols++;
+	
+	/*data_cols[send_on_cback_col= n_data_cols]=&str_send_on_cback_col;
+	data_vals[send_on_cback_col].type = DB1_INT;
+	data_vals[send_on_cback_col].nul = 0;
+	data_vals[send_on_cback_col].val.int_val= s->send_on_cback;
+	n_data_cols++;
+
+	data_cols[db_flag_col= n_data_cols]=&str_db_flag_col;
+	data_vals[db_flag_col].type = DB1_INT;
+	data_vals[db_flag_col].nul = 0;
+	data_vals[db_flag_col].val.int_val= s->db_flag;
+	n_data_cols++;*/
+
+	if(rls2_dbf.insert(rls2_db, data_cols, data_vals, n_data_cols) < 0)
+	{
+		LM_ERR("db insert failed\n");
+		return(-1);
+	}
+
+	if (rlsdb_debug) LM_ERR( "Done\n" );
+	return(0);
+}
+
+/******************************************************************************/
+
+int matches_in_rlsdb( str callid, str to_tag, str from_tag ) 
+
+{
+ 	db1_res_t *result= NULL;
+	db_key_t query_cols[3];
+	db_val_t query_vals[3];
+	db_key_t result_cols[1];
+	int n_query_cols = 0;
+	int callid_col, totag_col, fromtag_col;
+	int rval;
+
+	if (rlsdb_debug)
+		LM_ERR( "matches_in_rlsdb callid=%.*s \nto_tag=%.*s \nfrom_tag=%.*s\n",
+		callid.len, callid.s,
+		to_tag.len, to_tag.s,
+		from_tag.len, from_tag.s );
+
+	if(rls2_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		return(-1);
+	}
+
+	if(rls2_dbf.use_table(rls2_db, &rlsubs_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		return(-1);
+	}
+	
+	query_cols[callid_col= n_query_cols] = &str_callid_col;
+	query_vals[callid_col].type = DB1_STR;
+	query_vals[callid_col].nul = 0;
+	query_vals[callid_col].val.str_val= callid;
+	n_query_cols++;
+
+	query_cols[totag_col= n_query_cols] = &str_to_tag_col;
+	query_vals[totag_col].type = DB1_STR;
+	query_vals[totag_col].nul = 0;
+	query_vals[totag_col].val.str_val= to_tag;
+	n_query_cols++;
+
+	query_cols[fromtag_col= n_query_cols] = &str_from_tag_col;
+	query_vals[fromtag_col].type = DB1_STR;
+	query_vals[fromtag_col].nul = 0;
+	query_vals[fromtag_col].val.str_val= from_tag;
+	n_query_cols++;
+	
+	result_cols[0]= &str_callid_col; /* should use id column instead here */
+	
+	if(rls2_dbf.query(rls2_db, query_cols, 0, query_vals, result_cols, 
+			n_query_cols, 1, 0, &result )< 0)
+	{
+		LM_ERR("Can't query db\n");
+		if(result) rls2_dbf.free_result(rls2_db, result);
+		return(-1);
+	}
+
+	if(result == NULL) return(-1);
+
+	rval = result->n;
+	rls2_dbf.free_result(rls2_db, result);
+	if (rlsdb_debug) LM_ERR( "Done matches=%d\n", rval );
+	return(rval);
+}
+
+/******************************************************************************/
+
+subs_t *get_dialog_rlsdb( str callid, str to_tag, str from_tag ) 
+
+{
+ 	db_key_t query_cols[3];
+	db_val_t query_vals[3];
+	db_key_t result_cols[22];
+	int n_query_cols = 0, n_result_cols=0;
+	int callid_col, totag_col, fromtag_col;	
+	int r_pres_uri_col,r_to_user_col,r_to_domain_col;
+	int r_from_user_col,r_from_domain_col,r_callid_col;
+	int r_to_tag_col,r_from_tag_col,r_socket_info_col;
+	int r_event_id_col,r_local_contact_col,r_contact_col;
+	int r_record_route_col, r_reason_col;
+	int r_event_col, r_local_cseq_col, r_remote_cseq_col;
+	int r_status_col, r_version_col;
+	int r_expires_col;
+	db1_res_t *result= NULL;
+ 	db_val_t *values;
+	db_row_t *rows;
+	int nr_rows, size, loop;
+	subs_t *dest;
+	event_t parsed_event;
+	str ev_sname;
+
+	if (rlsdb_debug)
+		LM_ERR( "get_dialog_rlsdb callid=%.*s \nto_tag=%.*s \nfrom_tag=%.*s\n",
+		callid.len, callid.s,
+		to_tag.len, to_tag.s,
+		from_tag.len, from_tag.s );
+
+	if(rls2_db == NULL)
+	{
+		LM_ERR("null database connection\n");
+		return(NULL);
+	}
+
+	if(rls2_dbf.use_table(rls2_db, &rlsubs_table)< 0)
+	{
+		LM_ERR("use table failed\n");
+		return(NULL);
+	}
+	
+	query_cols[callid_col= n_query_cols] = &str_callid_col;
+	query_vals[callid_col].type = DB1_STR;
+	query_vals[callid_col].nul = 0;
+	query_vals[callid_col].val.str_val= callid;
+	n_query_cols++;
+
+	query_cols[totag_col= n_query_cols] = &str_to_tag_col;
+	query_vals[totag_col].type = DB1_STR;
+	query_vals[totag_col].nul = 0;
+	query_vals[totag_col].val.str_val= to_tag;
+	n_query_cols++;
+
+	query_cols[fromtag_col= n_query_cols] = &str_from_tag_col;
+	query_vals[fromtag_col].type = DB1_STR;
+	query_vals[fromtag_col].nul = 0;
+	query_vals[fromtag_col].val.str_val= from_tag;
+	n_query_cols++;
+	
+	result_cols[r_pres_uri_col=n_result_cols++] = &str_presentity_uri_col;
+	result_cols[r_to_user_col=n_result_cols++] = &str_to_user_col;
+	result_cols[r_to_domain_col=n_result_cols++] = &str_to_domain_col;
+	result_cols[r_from_user_col=n_result_cols++] = &str_watcher_username_col;
+	result_cols[r_from_domain_col=n_result_cols++] = &str_watcher_domain_col;
+	result_cols[r_callid_col=n_result_cols++] = &str_callid_col;
+	result_cols[r_to_tag_col=n_result_cols++] = &str_to_tag_col;
+	result_cols[r_from_tag_col=n_result_cols++] = &str_from_tag_col;
+	result_cols[r_socket_info_col=n_result_cols++] = &str_socket_info_col;
+	result_cols[r_event_id_col=n_result_cols++] = &str_event_id_col;
+	result_cols[r_local_contact_col=n_result_cols++] = &str_local_contact_col;
+	result_cols[r_contact_col=n_result_cols++] = &str_contact_col;
+	result_cols[r_record_route_col=n_result_cols++] = &str_record_route_col;
+	result_cols[r_reason_col=n_result_cols++] = &str_reason_col;
+	result_cols[r_event_col=n_result_cols++] = &str_event_col;
+	result_cols[r_local_cseq_col=n_result_cols++] = &str_local_cseq_col;
+	result_cols[r_remote_cseq_col=n_result_cols++] = &str_remote_cseq_col;
+	result_cols[r_status_col=n_result_cols++] = &str_status_col;
+	result_cols[r_version_col=n_result_cols++] = &str_version_col;
+	/*result_cols[r_send_on_cback_col=n_result_cols++] = &str_send_on_cback_col;*/
+	result_cols[r_expires_col=n_result_cols++] = &str_expires_col;
+
+
+	if(rls2_dbf.query(rls2_db, query_cols, 0, query_vals, result_cols, 
+				n_query_cols, n_result_cols, 0, &result )< 0)
+	{
+		LM_ERR("Can't query db\n");
+		if(result) rls2_dbf.free_result(rls2_db, result);
+		return(NULL);
+	}
+
+	if(result == NULL) return(NULL);
+
+	nr_rows = RES_ROW_N(result);
+
+	if (nr_rows == 0)
+	{
+		/* no match */ 
+		LM_ERR( "get_dialog_rlsb No matching records\n" );
+		rls2_dbf.free_result(rls2_db, result);
+		return(NULL);
+	}
+
+	if (nr_rows != 1)
+	{
+		LM_ERR( "get_dialog_rlsb multiple matching records\n" );
+		rls2_dbf.free_result(rls2_db, result);
+		return(NULL);
+	}
+
+	/* get the results and fill in return data structure */
+	for (loop=0; loop <nr_rows; loop++)
+	{
+		rows = RES_ROWS(result);
+		values = ROW_VALUES(rows);
+
+		size= sizeof(subs_t) +
+			( strlen(VAL_STRING(values+r_pres_uri_col))
+			+ strlen(VAL_STRING(values+r_to_user_col))
+			+ strlen(VAL_STRING(values+r_to_domain_col))
+			+ strlen(VAL_STRING(values+r_from_user_col))
+			+ strlen(VAL_STRING(values+r_from_domain_col))
+			+ strlen(VAL_STRING(values+r_to_tag_col))
+			+ strlen(VAL_STRING(values+r_from_tag_col))
+			+ strlen(VAL_STRING(values+r_callid_col))
+			+ strlen(VAL_STRING(values+r_socket_info_col))
+			+ strlen(VAL_STRING(values+r_local_contact_col))
+			+ strlen(VAL_STRING(values+r_contact_col))
+			+ strlen(VAL_STRING(values+r_record_route_col))
+			+ strlen(VAL_STRING(values+r_event_id_col))
+			+ strlen(VAL_STRING(values+r_reason_col))+ 1)*sizeof(char);
+
+		dest= (subs_t*)pkg_malloc(size);
+	
+		if(dest== NULL)
+		{
+			LM_ERR( "Can't allocate memory\n" );
+			/* tidy up and return >>>>>>>>>>>>>>>>>>>> */
+			rls2_dbf.free_result(rls2_db, result);
+			return(NULL);
+		}
+		memset(dest, 0, size);
+		size= sizeof(subs_t);
+
+		CONT_COPYDB(dest, dest->pres_uri, VAL_STRING(values+r_pres_uri_col))
+		CONT_COPYDB(dest, dest->to_user, VAL_STRING(values+r_to_user_col))
+		CONT_COPYDB(dest, dest->to_domain, VAL_STRING(values+r_to_domain_col))
+		CONT_COPYDB(dest, dest->from_user, VAL_STRING(values+r_from_user_col))
+		CONT_COPYDB(dest, dest->from_domain, VAL_STRING(values+r_from_domain_col))
+		CONT_COPYDB(dest, dest->to_tag, VAL_STRING(values+r_to_tag_col))
+		CONT_COPYDB(dest, dest->from_tag, VAL_STRING(values+r_from_tag_col))
+		CONT_COPYDB(dest, dest->callid, VAL_STRING(values+r_callid_col))
+		CONT_COPYDB(dest, dest->sockinfo_str, VAL_STRING(values+r_socket_info_col))
+		CONT_COPYDB(dest, dest->local_contact, VAL_STRING(values+r_local_contact_col))
+		CONT_COPYDB(dest, dest->contact, VAL_STRING(values+r_contact_col))
+		CONT_COPYDB(dest, dest->record_route, VAL_STRING(values+r_record_route_col))
+
+		if(strlen(VAL_STRING(values+r_event_id_col)) > 0)
+			CONT_COPYDB(dest, dest->event_id, VAL_STRING(values+r_event_id_col))
+
+		if(strlen(VAL_STRING(values+r_reason_col)) > 0)
+			CONT_COPYDB(dest, dest->reason, VAL_STRING(values+r_reason_col))
+
+
+		ev_sname.s= (char *)VAL_STRING(values+r_event_col);
+		ev_sname.len= strlen(ev_sname.s);
+		
+		dest->event = pres_contains_event(&ev_sname, &parsed_event);
+
+		if(dest->event == NULL)
+		{
+			LM_ERR("event not found and set to NULL\n");
+		}
+
+		dest->local_cseq= VAL_INT(values+r_local_cseq_col);
+		dest->remote_cseq= VAL_INT(values+r_remote_cseq_col);
+		dest->status= VAL_INT(values+r_status_col);
+		dest->version= VAL_INT(values+r_version_col);
+		/*dest->send_on_cback= VAL_INT(values+r_send_on_cback_col);*/
+		dest->expires= VAL_INT(values+r_expires_col);
+
+		/*dest->db_flag= VAL_INT(values+r_db_flag_col);*/
+	}
+
+	rls2_dbf.free_result(rls2_db, result);
+
+	if (rlsdb_debug) LM_ERR( "Done\n" );
+	return(dest);
+}
+
+/******************************************************************************/
+
+void dump_dialog( subs_t *s )
+
+{
+	if (!rlsdb_debug) return;
+	LM_ERR( "pres_rui=%.*s\n", s->pres_uri.len, s->pres_uri.s );
+	LM_ERR( "to_user=%.*s\n", s->to_user.len, s->to_user.s );
+	LM_ERR( "to_domain=%.*s\n", s->to_domain.len, s->to_domain.s );
+	LM_ERR( "from_user=%.*s\n", s->from_user.len, s->from_user.s );
+	LM_ERR( "from_domain=%.*s\n", s->from_domain.len, s->from_domain.s );
+	LM_ERR( "to_tag=%.*s\n", s->to_tag.len, s->to_tag.s );
+	LM_ERR( "from_tag=%.*s\n", s->from_tag.len, s->from_tag.s );
+	LM_ERR( "callid=%.*s\n", s->callid.len, s->callid.s );
+	LM_ERR( "sockinfo_str=%.*s\n", s->sockinfo_str.len, s->sockinfo_str.s );
+	LM_ERR( "local_contact=%.*s\n", s->local_contact.len, s->local_contact.s );
+	LM_ERR( "contact=%.*s\n", s->contact.len, s->contact.s );
+	LM_ERR( "record_route=%.*s\n", s->record_route.len, s->record_route.s );
+	LM_ERR( "event_id=%.*s\n", s->event_id.len, s->event_id.s );
+	LM_ERR( "reason=%.*s\n", s->reason.len, s->reason.s );
+	LM_ERR( "event=%.*s\n", s->event->name.len, s->event->name.s );
+	LM_ERR( "local_cseq=%d\n", s-> local_cseq);
+	LM_ERR( "remote_cseq=%d\n", s->remote_cseq );
+	LM_ERR( "status=%d\n", s->status );
+	LM_ERR( "version=%d\n", s->version );
+	LM_ERR( "expires=%d\n", s->expires );
+	LM_ERR( "send_on_cback=%d\n", s->send_on_cback );
+	LM_ERR( "db_flag=%x\n", s->db_flag );
+}
+
+/******************************************************************************/
+

+ 123 - 56
modules_k/rls/subscribe.c

@@ -154,7 +154,7 @@ int rls_get_service_list(str *service_uri, str *user, str *domain,
 	LM_DBG("searching document for user sip:%.*s@%.*s\n",
 	LM_DBG("searching document for user sip:%.*s@%.*s\n",
 		user->len, user->s, domain->len, domain->s);
 		user->len, user->s, domain->len, domain->s);
 
 
-	if(rls_dbf.use_table(rls_db, &rls_xcap_table) < 0)
+	if(rls_xcap_dbf.use_table(rls_xcap_db, &rls_xcap_table) < 0)
 	{
 	{
 		LM_ERR("in use_table-[table]= %.*s\n",
 		LM_ERR("in use_table-[table]= %.*s\n",
 				rls_xcap_table.len, rls_xcap_table.s);
 				rls_xcap_table.len, rls_xcap_table.s);
@@ -164,13 +164,13 @@ int rls_get_service_list(str *service_uri, str *user, str *domain,
 	result_cols[xcap_col= n_result_cols++] = &str_doc_col;
 	result_cols[xcap_col= n_result_cols++] = &str_doc_col;
 	result_cols[etag_col= n_result_cols++] = &str_etag_col;
 	result_cols[etag_col= n_result_cols++] = &str_etag_col;
 
 
-	if(rls_dbf.query(rls_db, query_cols, 0 , query_vals, result_cols,
+	if(rls_xcap_dbf.query(rls_xcap_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)
 	{
 	{
 		LM_ERR("failed querying table xcap for document [service_uri]=%.*s\n",
 		LM_ERR("failed querying table xcap for document [service_uri]=%.*s\n",
 				service_uri->len, service_uri->s);
 				service_uri->len, service_uri->s);
 		if(result)
 		if(result)
-			rls_dbf.free_result(rls_db, result);
+			rls_xcap_dbf.free_result(rls_xcap_db, result);
 		return -1;
 		return -1;
 	}
 	}
 
 
@@ -180,7 +180,7 @@ int rls_get_service_list(str *service_uri, str *user, str *domain,
 		
 		
 		if(rls_integrated_xcap_server)
 		if(rls_integrated_xcap_server)
 		{
 		{
-			rls_dbf.free_result(rls_db, result);
+			rls_xcap_dbf.free_result(rls_xcap_db, result);
 			return 0;
 			return 0;
 		}
 		}
 		
 		
@@ -254,7 +254,7 @@ int rls_get_service_list(str *service_uri, str *user, str *domain,
 		*rootdoc = xmldoc;
 		*rootdoc = xmldoc;
 	}
 	}
 
 
-	rls_dbf.free_result(rls_db, result);
+	rls_xcap_dbf.free_result(rls_xcap_db, result);
 	if(xcapdoc!=NULL)
 	if(xcapdoc!=NULL)
 		pkg_free(xcapdoc);
 		pkg_free(xcapdoc);
 
 
@@ -262,7 +262,7 @@ int rls_get_service_list(str *service_uri, str *user, str *domain,
 
 
 error:
 error:
 	if(result!=NULL)
 	if(result!=NULL)
-		rls_dbf.free_result(rls_db, result);
+		rls_xcap_dbf.free_result(rls_xcap_db, result);
 	if(xmldoc!=NULL)
 	if(xmldoc!=NULL)
 		xmlFreeDoc(xmldoc);
 		xmlFreeDoc(xmldoc);
 	if(xcapdoc!=NULL)
 	if(xcapdoc!=NULL)
@@ -417,7 +417,7 @@ int rls_handle_subscribe(struct sip_msg* msg, char* s1, char* s2)
 	str* contact = NULL;
 	str* contact = NULL;
 	xmlDocPtr doc = NULL;
 	xmlDocPtr doc = NULL;
 	xmlNodePtr service_node = NULL;
 	xmlNodePtr service_node = NULL;
-	unsigned int hash_code;
+	unsigned int hash_code=0;
 	int to_tag_gen = 0;
 	int to_tag_gen = 0;
 	event_t* parsed_event;
 	event_t* parsed_event;
 	param_t* ev_param = NULL;
 	param_t* ev_param = NULL;
@@ -544,20 +544,33 @@ int rls_handle_subscribe(struct sip_msg* msg, char* s1, char* s2)
 		}
 		}
 	} else {
 	} else {
 		/* search if a stored dialog */
 		/* search if a stored dialog */
-		hash_code = core_hash(&msg->callid->body,
-				&get_to(msg)->tag_value, hash_size);
-		lock_get(&rls_table[hash_code].lock);
-
-		if(pres_search_shtable(rls_table, msg->callid->body,
-				get_to(msg)->tag_value, get_from(msg)->tag_value,
-				hash_code)==NULL)
+		if ( dbmode == RLS_DB_ONLY )
+		{
+			if (matches_in_rlsdb(msg->callid->body,get_to(msg)->tag_value,
+							 get_from(msg)->tag_value ) <= 0 )
+			{
+				LM_DBG("subscription dialog not found for <%.*s>\n",
+						get_from(msg)->uri.len, get_from(msg)->uri.s);
+				goto forpresence;
+			}
+		}
+		else
 		{
 		{
+			hash_code = core_hash(&msg->callid->body,
+					&get_to(msg)->tag_value, hash_size);
+			lock_get(&rls_table[hash_code].lock);
+
+			if(pres_search_shtable(rls_table, msg->callid->body,
+					get_to(msg)->tag_value, get_from(msg)->tag_value,
+					hash_code)==NULL)
+			{
+				lock_release(&rls_table[hash_code].lock);
+				LM_DBG("subscription dialog not found for <%.*s>\n",
+						get_from(msg)->uri.len, get_from(msg)->uri.s);
+				goto forpresence;
+			}
 			lock_release(&rls_table[hash_code].lock);
 			lock_release(&rls_table[hash_code].lock);
-			LM_DBG("subscription dialog not found for <%.*s>\n",
-					get_from(msg)->uri.len, get_from(msg)->uri.s);
-			goto forpresence;
 		}
 		}
-		lock_release(&rls_table[hash_code].lock);
 	}
 	}
 
 
 	/* extract dialog information from message headers */
 	/* extract dialog information from message headers */
@@ -572,7 +585,8 @@ int rls_handle_subscribe(struct sip_msg* msg, char* s1, char* s2)
 	if(reply_200(msg, &subs.local_contact, subs.expires)<0)
 	if(reply_200(msg, &subs.local_contact, subs.expires)<0)
 		goto error;
 		goto error;
 
 
-	hash_code = core_hash(&subs.callid, &subs.to_tag, hash_size);
+	if (dbmode != RLS_DB_ONLY)
+		hash_code = core_hash(&subs.callid, &subs.to_tag, hash_size);
 
 
 	if(get_to(msg)->tag_value.s==NULL || get_to(msg)->tag_value.len==0)
 	if(get_to(msg)->tag_value.s==NULL || get_to(msg)->tag_value.len==0)
 	{ /* initial subscribe */
 	{ /* initial subscribe */
@@ -581,14 +595,29 @@ int rls_handle_subscribe(struct sip_msg* msg, char* s1, char* s2)
 		if(subs.expires != 0)
 		if(subs.expires != 0)
 		{
 		{
 			subs.version = 1;
 			subs.version = 1;
-			if(pres_insert_shtable(rls_table, hash_code, &subs)<0)
+			if (dbmode==RLS_DB_ONLY)
+			{
+				rt=insert_rlsdb( &subs );
+			}
+			else
+			{
+				rt=pres_insert_shtable(rls_table, hash_code, &subs);
+			}
+			if (rt<0)
 			{
 			{
 				LM_ERR("while adding new subscription\n");
 				LM_ERR("while adding new subscription\n");
 				goto error;
 				goto error;
 			}
 			}
 		}
 		}
 	} else {
 	} else {
-		rt = update_rlsubs(&subs, hash_code);
+		if (dbmode==RLS_DB_ONLY)
+		{
+			rt=update_subs_rlsdb( &subs );
+		}
+		else
+		{
+			rt = update_rlsubs(&subs, hash_code);
+		}
 		if(rt<0)
 		if(rt<0)
 		{
 		{
 			LM_ERR("while updating resource list subscription\n");
 			LM_ERR("while updating resource list subscription\n");
@@ -634,7 +663,19 @@ int rls_handle_subscribe(struct sip_msg* msg, char* s1, char* s2)
 		LM_ERR("failed sending subscribe requests to resources in list\n");
 		LM_ERR("failed sending subscribe requests to resources in list\n");
 		goto error;
 		goto error;
 	}
 	}
-	remove_expired_rlsubs(&subs, hash_code);
+
+	if (dbmode==RLS_DB_ONLY)
+	{
+		if(subs.expires==0)
+		{
+			LM_ERR( "rls_db removing expired rls subs\n" );
+			delete_rlsdb( &subs.callid, &subs.to_tag, &subs.from_tag );
+		}
+	}
+	else
+	{
+		remove_expired_rlsubs(&subs, hash_code);
+	}
 
 
 done:
 done:
 	if(contact!=NULL)
 	if(contact!=NULL)
@@ -693,6 +734,11 @@ int remove_expired_rlsubs( subs_t* subs, unsigned int hash_code)
 	if(subs->expires!=0)
 	if(subs->expires!=0)
 		return 0;
 		return 0;
 
 
+	if (dbmode == RLS_DB_ONLY)
+	{
+		LM_ERR( "remove_expired_rlsubs called in RLS_DB_ONLY mode\n" );
+	} 
+
 	/* search the record in hash table */
 	/* search the record in hash table */
 	lock_get(&rls_table[hash_code].lock);
 	lock_get(&rls_table[hash_code].lock);
 
 
@@ -734,6 +780,11 @@ int update_rlsubs( subs_t* subs, unsigned int hash_code)
 {
 {
 	subs_t* s;
 	subs_t* s;
 
 
+	if (dbmode == RLS_DB_ONLY)
+	{
+		LM_ERR( "update_rlsubs called in RLS_DB_ONLY mode\n" );
+	} 
+
 	/* search the record in hash table */
 	/* search the record in hash table */
 	lock_get(&rls_table[hash_code].lock);
 	lock_get(&rls_table[hash_code].lock);
 
 
@@ -886,6 +937,48 @@ int fixup_update_subs(void** param, int param_no)
 	return 0;
 	return 0;
 }
 }
 
 
+void update_a_sub(subs_t *subs_copy )
+
+{
+	xmlDocPtr doc = NULL;
+	xmlNodePtr service_node = NULL;
+
+	if ((subs_copy->expires -= (int)time(NULL)) <= 0)
+	{
+		LM_WARN("found expired subscription for: %.*s\n",
+			subs_copy->pres_uri.len, subs_copy->pres_uri.s);
+		goto done;
+	}
+
+	if(rls_get_service_list(&subs_copy->pres_uri, &subs_copy->from_user,
+				&subs_copy->from_domain, &service_node, &doc)<0)
+	{
+		LM_ERR("failed getting resource list for: %.*s\n",
+			subs_copy->pres_uri.len, subs_copy->pres_uri.s);
+		goto done;
+	}
+
+	if(doc==NULL)
+	{
+		LM_WARN("no document returned for: %.*s\n",
+			subs_copy->pres_uri.len, subs_copy->pres_uri.s);
+		goto done;
+	}
+
+	if(resource_subscriptions(subs_copy, service_node)< 0)
+	{
+		LM_ERR("failed sending subscribe requests to resources in list\n");
+		goto done;
+	}
+
+done:
+	if (doc != NULL)
+		xmlFreeDoc(doc);
+
+	pkg_free(subs_copy);
+}
+
+
 int rls_update_subs(struct sip_msg *msg, char *puri, char *pevent)
 int rls_update_subs(struct sip_msg *msg, char *puri, char *pevent)
 {
 {
 	str uri;
 	str uri;
@@ -935,6 +1028,12 @@ int rls_update_subs(struct sip_msg *msg, char *puri, char *pevent)
 		parsed_uri.user.len, parsed_uri.user.s,
 		parsed_uri.user.len, parsed_uri.user.s,
 		parsed_uri.host.len, parsed_uri.host.s);
 		parsed_uri.host.len, parsed_uri.host.s);
 
 
+	if (dbmode==RLS_DB_ONLY)
+	{
+		return(update_all_subs_rlsdb(&parsed_uri.user, &parsed_uri.host, &event));
+	}
+
+
 	if (rls_table == NULL)
 	if (rls_table == NULL)
 	{
 	{
 		LM_ERR("rls_table is NULL\n");
 		LM_ERR("rls_table is NULL\n");
@@ -959,9 +1058,7 @@ int rls_update_subs(struct sip_msg *msg, char *puri, char *pevent)
 				subs->event->evp->type == e.type)
 				subs->event->evp->type == e.type)
 			{
 			{
 				subs_t *subs_copy = NULL;
 				subs_t *subs_copy = NULL;
-				xmlDocPtr doc = NULL;
-				xmlNodePtr service_node = NULL;
-	
+
 				LM_DBG("found matching RLS subscription for: %.*s\n",
 				LM_DBG("found matching RLS subscription for: %.*s\n",
 					subs->pres_uri.len, subs->pres_uri.s);
 					subs->pres_uri.len, subs->pres_uri.s);
 
 
@@ -972,37 +1069,7 @@ int rls_update_subs(struct sip_msg *msg, char *puri, char *pevent)
 					return -1;
 					return -1;
 				}
 				}
 
 
-				if ((subs_copy->expires -= (int)time(NULL)) <= 0)
-				{
-					LM_WARN("found expired subscription for: %.*s\n",
-						subs_copy->pres_uri.len, subs_copy->pres_uri.s);
-					goto loop_done;
-				}
-
-				if(rls_get_service_list(&subs_copy->pres_uri, &subs_copy->from_user,
-							&subs_copy->from_domain, &service_node, &doc)<0)
-				{
-					LM_ERR("failed getting resource list for: %.*s\n",
-						subs_copy->pres_uri.len, subs_copy->pres_uri.s);
-					goto loop_done;
-				}
-				if(doc==NULL)
-				{
-					LM_WARN("no document returned for: %.*s\n",
-						subs_copy->pres_uri.len, subs_copy->pres_uri.s);
-					goto loop_done;
-				}
-
-				if(resource_subscriptions(subs_copy, service_node)< 0)
-				{
-					LM_ERR("failed sending subscribe requests to resources in list\n");
-					goto loop_done;
-				}
-
-loop_done:
-				if (doc != NULL)
-					xmlFreeDoc(doc);
-				pkg_free(subs_copy);
+				update_a_sub(subs_copy);
 			}
 			}
 			subs = subs->next;
 			subs = subs->next;
 		}		
 		}