Browse Source

modules_k/pua: added DB only mode to PUA

- By using DB only mode PUA (and modules that use PUA like 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.
pd 14 years ago
parent
commit
90e9d7fc8f

+ 121 - 97
modules_k/pua/README

@@ -1,3 +1,4 @@
+
 Presence User Agent Module
 
 Anca-Maria Vamanu
@@ -8,8 +9,8 @@ Edited by
 
 Anca-Maria Vamanu
 
-   Copyright © 2006 Voice Sistem SRL
-     __________________________________________________________________
+   Copyright © 2006 Voice Sistem SRL
+     _________________________________________________________________
 
    Table of Contents
 
@@ -32,22 +33,23 @@ Anca-Maria Vamanu
               3.7. outbound_proxy (str)
               3.8. dlginfo_increase_version (int)
               3.9. reginfo_increase_version (int)
-              3.10. check_remote_contact (int)
+              3.10. db_mode (int)
+              3.11. check_remote_contact (int)
 
         4. Functions
 
-              4.1. pua_update_contact()
+              4.1. pua_update_contact() 
 
         5. Installation
 
    2. Developer Guide
 
-        1. bind_pua(pua_api_t* api)
-        2. send_publish
-        3. send_subscribe
-        4. is_dialog
-        5. register_puacb
-        6. add_event
+        1. bind_pua(pua_api_t* api) 
+        2. send_publish 
+        3. send_subscribe 
+        4. is_dialog 
+        5. register_puacb 
+        6. add_event 
 
    List of Examples
 
@@ -60,8 +62,9 @@ Anca-Maria Vamanu
    1.7. Set outbound_proxy parameter
    1.8. Set dlginfo_increase_version parameter
    1.9. Set reginfo_increase_version parameter
-   1.10. Set check_remote_contact parameter
-   1.11. pua_update_contact usage
+   1.10. Set db_mode parameter
+   1.11. Set check_remote_contact parameter
+   1.12. pua_update_contact usage
    2.1. pua_api structure
    2.2. pua_is_dialog usage example
    2.3. register_puacb usage example
@@ -88,37 +91,41 @@ Chapter 1. Admin Guide
         3.7. outbound_proxy (str)
         3.8. dlginfo_increase_version (int)
         3.9. reginfo_increase_version (int)
-        3.10. check_remote_contact (int)
+        3.10. db_mode (int)
+        3.11. check_remote_contact (int)
 
    4. Functions
 
-        4.1. pua_update_contact()
+        4.1. pua_update_contact() 
 
    5. Installation
 
 1. Overview
 
-   This module offer the functionality of a presence user agent client,
+   This  module  offer the functionality of a presence user agent client,
    sending Subscribe and Publish messages. It's a core part of Kamailio's
-   SIP presence package, implementing SIMPLE and various shared line
+   SIP  presence  package,  implementing  SIMPLE  and various shared line
    appearance implementations.
 
-   It can be used with the following modules: pua_mi and pua_usrloc,
-   pua_bla and pua_xmpp. The <emphasize>pua_mi</emphasize> offer the
-   possibility to publish any kind of information or subscribing to a
+   It  can  be  used  with  the following modules: pua_mi and pua_usrloc,
+   pua_bla  and  pua_xmpp.  The  <emphasize>pua_mi</emphasize>  offer the
+   possibility  to  publish  any  kind of information or subscribing to a
    resource through the manager interface. The
-   <emphasize>pua_usrloc</emphasize> module calls a function exported by
-   pua modules to publish elementary presence information, such as basic
-   status "open" or "closed", for clients that do not implement
-   client-to-server presence. Through <emphasize>pua_bla</emphasize> ,
-   BRIDGED LINE APPEARANCE features are added to openser. The
-   <emphasize>pua_xmpp</emphasize> module represents a gateway between SIP
-   and XMPP, so that jabber and SIP clients can exchange presence
+   <emphasize>pua_usrloc</emphasize>  module calls a function exported by
+   pua  modules to publish elementary presence information, such as basic
+   status   "open"  or  "closed",  for  clients  that  do  not  implement
+   client-to-server  presence.  Through  <emphasize>pua_bla</emphasize> ,
+   BRIDGED   LINE   APPEARANCE   features   are  added  to  openser.  The
+   <emphasize>pua_xmpp</emphasize>  module  represents  a gateway between
+   SIP  and  XMPP,  so  that jabber and SIP clients can exchange presence
    information.
 
-   The module use a cache to store the presentity list and writes to
-   database on timer to be able to recover upon restart. The presence is
-   kept in-memory during run-time.
+   The module supports 2 modes of operation. In the first a cache is used
+   to  store  the  presentity  list and writes to database on timer to be
+   able  to  recover  upon restart. The presence is kept in-memory during
+   run-time. The second is a database only mode where the presentity list
+   is  stored  purely  in  a  database,  allowing  both  scalability  and
+   resilience.
 
 2. Dependencies
 
@@ -133,7 +140,7 @@ Chapter 1. Admin Guide
 
 2.2. External Libraries or Applications
 
-   The following libraries or applications must be installed before
+   The  following  libraries  or  applications  must  be installed before
    running Kamailio with this module loaded:
      * libxml.
 
@@ -148,15 +155,16 @@ Chapter 1. Admin Guide
    3.7. outbound_proxy (str)
    3.8. dlginfo_increase_version (int)
    3.9. reginfo_increase_version (int)
-   3.10. check_remote_contact (int)
+   3.10. db_mode (int)
+   3.11. check_remote_contact (int)
 
 3.1. hash_size (int)
 
-   The size of the hash table used for storing Subscribe and Publish
-   information. This parameter will be used as the power of 2 when
+   The  size  of  the  hash  table used for storing Subscribe and Publish
+   information.  This  parameter  will  be  used  as  the power of 2 when
    computing table size.
 
-   Default value is “9�.
+   Default value is "9". 
 
    Example 1.1. Set hash_size parameter
 ...
@@ -167,7 +175,7 @@ modparam("pua", "hash_size", 11)
 
    Database url.
 
-   Default value is “>mysql://openser:openserrw@localhost/openser�.
+   Default value is ">mysql://openser:openserrw@localhost/openser". 
 
    Example 1.2. Set db_url parameter
 ...
@@ -178,7 +186,7 @@ modparam("pua", "db_url" "dbdriver://username:password@dbhost/dbname")
 
    The name of the database table.
 
-   Default value is “pua�.
+   Default value is "pua". 
 
    Example 1.3. Set db_table parameter
 ...
@@ -189,7 +197,7 @@ modparam("pua", "db_table", "pua")
 
    The inferior expires limit for both Publish and Subscribe.
 
-   Default value is “0�.
+   Default value is "0". 
 
    Example 1.4. Set min_expires parameter
 ...
@@ -198,10 +206,10 @@ modparam("pua", "min_expires", 0)
 
 3.5. default_expires (int)
 
-   The default expires value used in case this information is not
+   The  default  expires  value  used  in  case  this  information is not
    provisioned.
 
-   Default value is “3600�.
+   Default value is "3600". 
 
    Example 1.5. Set default_expires parameter
 ...
@@ -210,11 +218,12 @@ modparam("pua", "default_expires", 3600)
 
 3.6. update_period (int)
 
-   The interval at which the information in database and hash table should
-   be updated. In the case of the hash table updating means deleting
-   expired messages.
+   The  interval  at  which  the  information  in database and hash table
+   should  be  updated.  In  the  case  of  the hash table updating means
+   deleting expired messages. Setting a value less than or equal to zero,
+   disables updates.
 
-   Default value is “100�.
+   Default value is "100". 
 
    Example 1.6. Set update_period parameter
 ...
@@ -225,7 +234,7 @@ modparam("pua", "update_period", 100)
 
    SIP URI of outbound proxy to be used when sending PUBLISH requests.
 
-   By default, no outbound proxy has been defined.
+   By default, no outbound proxy has been defined. 
 
    Example 1.7. Set outbound_proxy parameter
 ...
@@ -234,16 +243,16 @@ modparam("pua", "outbound_proxy", "sip:outbound.example.com")
 
 3.8. dlginfo_increase_version (int)
 
-   When sending PUBLISHs for Event: dialog, the body contains an XML
-   document according to RFC 4235. This XML document contains a version
-   attribute to easily detect changes in the dialog state. By setting this
-   parameter, the pua module parses the XML document and sets the version
-   attribute to the proper value. If the receiver of the PUBLISH does not
-   care about the version parameter (e.g. like Kamailio
-   presence_dialoginfo module) it makes no sense to waste CPU resources
+   When  sending  PUBLISHs  for  Event:  dialog, the body contains an XML
+   document  according  to RFC 4235. This XML document contains a version
+   attribute  to  easily  detect  changes in the dialog state. By setting
+   this  parameter,  the  pua module parses the XML document and sets the
+   version  attribute to the proper value. If the receiver of the PUBLISH
+   does  not  care  about  the  version  parameter  (e.g.  like  Kamailio
+   presence_dialoginfo  module)  it makes no sense to waste CPU resources
    for parsing the XML body and the parameter should be set to 0.
 
-   Default value is “0�.
+   Default value is "0". 
 
    Example 1.8. Set dlginfo_increase_version parameter
 ...
@@ -252,51 +261,66 @@ modparam("pua", "dlginfo_increase_version", 1)
 
 3.9. reginfo_increase_version (int)
 
-   When sending PUBLISHs for Event: reg, the body contains an XML document
-   according to RFC 4235(?). This XML document contains a version
-   attribute to easily detect changes in the registration state. By
-   setting this parameter, the pua module parses the XML document and sets
-   the version attribute to the proper value. If the receiver of the
-   PUBLISH does not care about the version parameter (e.g. like Kamailio
-   presence_reginfo module) it makes no sense to waste CPU resources for
+   When  sending  PUBLISHs  for  Event:  reg,  the  body  contains an XML
+   document  according  to  RFC  4235(?).  This  XML  document contains a
+   version  attribute to easily detect changes in the registration state.
+   By  setting this parameter, the pua module parses the XML document and
+   sets the version attribute to the proper value. If the receiver of the
+   PUBLISH  does not care about the version parameter (e.g. like Kamailio
+   presence_reginfo  module) it makes no sense to waste CPU resources for
    parsing the XML body and the parameter should be set to 0.
 
-   Default value is “0�.
+   Default value is "0". 
 
    Example 1.9. Set reginfo_increase_version parameter
 ...
 modparam("pua", "reginfo_increase_version", 1)
 ...
 
-3.10. check_remote_contact (int)
+3.10. 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.
+
+   Default value is "0". 
+
+   Example 1.10. Set db_mode parameter
+...
+modparam("pua", "db_mode", 0)
+...
+
+3.11. check_remote_contact (int)
 
    When sending a SUBSCRIBE check that the remote contact matches the one
    in the stored dialog or not. If the remote contact is checked and does
    not match the one in the stored dialog then the dialog is not matched.
    Checking the remote contact can cause problems when using modules like
-   RLS and should not be required in order to properly match the dialog
-   anyway. Set this parameter to 0 to disable checking of remote contact
+   RLS  and  should not be required in order to properly match the dialog
+   anyway.  Set this parameter to 0 to disable checking of remote contact
    for SUBSCRIBE dialog matching.
 
-   Default value is “1�.
+   Default value is "1". 
 
-   Example 1.10. Set check_remote_contact parameter
+   Example 1.11. Set check_remote_contact parameter
 ...
 modparam("pua", "check_remote_contact", 0)
 ...
 
 4. Functions
 
-   4.1. pua_update_contact()
+   4.1. pua_update_contact() 
 
 4.1.  pua_update_contact()
 
-   The remote target can be updated by the Contact of a subsequent in
-   dialog request. In the PUA watcher case (sending a SUBSCRIBE messages),
-   this means that the remote target for the following Subscribe messages
-   can be updated at any time by the contact of a Notify message. If this
-   function is called on request route on receiving a Notify message, it
-   will try to update the stored remote target.
+   The  remote  target  can  be updated by the Contact of a subsequent in
+   dialog   request.  In  the  PUA  watcher  case  (sending  a  SUBSCRIBE
+   messages),  this  means  that  the  remote  target  for  the following
+   Subscribe  messages  can  be  updated  at any time by the contact of a
+   Notify  message.  If  this  function  is  called  on  request route on
+   receiving  a  Notify  message, it will try to update the stored remote
+   target.
 
    This function can be used from REQUEST_ROUTE.
 
@@ -304,7 +328,7 @@ modparam("pua", "check_remote_contact", 0)
      * 1 - if success.
      * -1 - if error.
 
-   Example 1.11. pua_update_contact usage
+   Example 1.12. pua_update_contact usage
 ...
 if(method=="NOTIFY")
     pua_update_contact();
@@ -312,29 +336,29 @@ if(method=="NOTIFY")
 
 5. Installation
 
-   The module requires one table in the Kamailio database: pua. The SQL
-   syntax to create it can be found in presence_xml-create.sql script in
-   the database directories in the kamailio/scripts folder. You can also
-   find the complete database documentation on the project webpage,
+   The  module  requires one table in the Kamailio database: pua. The SQL
+   syntax  to create it can be found in presence_xml-create.sql script in
+   the  database directories in the kamailio/scripts folder. You can also
+   find  the  complete  database  documentation  on  the project webpage,
    http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html.
 
 Chapter 2. Developer Guide
 
    Table of Contents
 
-   1. bind_pua(pua_api_t* api)
-   2. send_publish
-   3. send_subscribe
-   4. is_dialog
-   5. register_puacb
-   6. add_event
+   1. bind_pua(pua_api_t* api) 
+   2. send_publish 
+   3. send_subscribe 
+   4. is_dialog 
+   5. register_puacb 
+   6. add_event 
 
-   The module provides the following functions that can be used by other
+   The  module provides the following functions that can be used by other
    Kamailio modules.
 
 1.  bind_pua(pua_api_t* api)
 
-   This function binds the pua modules and fills the structure with the
+   This  function  binds the pua modules and fills the structure with the
    two exported functions.
 
    Example 2.1. pua_api structure
@@ -355,8 +379,8 @@ typedef struct pua_api {
 typedef int (*send_publish_t)(publ_info_t* publ);
 ...
 
-   This function receives as a parameter a structure with Publish required
-   information and sends a Publish message.
+   This  function  receives  as  a  parameter  a  structure  with Publish
+   required information and sends a Publish message.
 
    The structure received as a parameter:
 ...
@@ -395,7 +419,7 @@ typedef struct publ_info
 typedef int (*send_subscribe_t)(subs_info_t* subs);
 ...
 
-   This function receives as a parameter a structure with Subscribe
+   This  function  receives  as  a  parameter  a structure with Subscribe
    required information and sends a Subscribe message.
 
    The structure received as a parameter:
@@ -434,8 +458,8 @@ typedef struct subs_info
 typedef int  (*query_dialog_t)(ua_pres_t* presentity);
 ...
 
-   This function checks is the parameter corresponds to a stored Subscribe
-   initiated dialog.
+   This  function  checks  is  the  parameter  corresponds  to  a  stored
+   Subscribe initiated dialog.
 
    Example 2.2. pua_is_dialog usage example
 ...
@@ -454,11 +478,11 @@ typedef int (*register_puacb_t)(int types, pua_cb f, void* param );
 ...
 
    This function registers a callback to be called on receiving the reply
-   message for a sent Publish or Subscribe request. The type parameter
-   should be set the same as the source_flag for that request. The
-   function registered as callback for pua should be of type pua_cb ,
+   message  for  a  sent Publish or Subscribe request. The type parameter
+   should  be  set  the  same  as  the  source_flag for that request. The
+   function  registered  as  callback  for pua should be of type pua_cb ,
    which is: typedef void (pua_cb)(ua_pres_t* hentity, struct msg_start *
-   fl); The parameters are the dialog structure for that request and the
+   fl);  The parameters are the dialog structure for that request and the
    first line of the reply message.
 
    Example 2.3. register_puacb usage example
@@ -491,9 +515,9 @@ typedef int (*add_pua_event_t)(int ev_flag, char* name,
                 (NULL if winfo event)
 ...
 
-   This function allows registering new events to the pua module. Now
-   there are 4 events supported by the pua module: presence,
-   presence;winfo, message-summary, dialog;sla. These events are
+   This  function  allows  registering  new events to the pua module. Now
+   there   are   4   events   supported  by  the  pua  module:  presence,
+   presence;winfo,   message-summary,   dialog;sla.   These   events  are
    registered from within the pua module.
 
    Filed type for process_body:

+ 28 - 4
modules_k/pua/doc/pua_admin.xml

@@ -34,9 +34,11 @@
 		 jabber and SIP clients can exchange presence information. 
 	</para>
 	<para>
-		The module use a cache to store the presentity list and writes to database
-		on timer to be able to recover upon restart. The presence is kept in-memory
-		during run-time.
+		The module supports 2 modes of operation. In the first a cache is used to store
+		the presentity list and writes to database on timer to be able to recover upon
+		restart. The presence is kept in-memory	during run-time. The second is a database
+		only mode where the presentity list is stored purely in a database, allowing
+		both scalability and resilience.
 	</para>
 	</section>
 		<section>
@@ -173,7 +175,8 @@ modparam("pua", "default_expires", 3600)
 		<para>
 		The interval at which the information in database and hash table
 		should be updated. In the case of the hash table updating means 
-		deleting expired messages.
+		deleting expired messages. 
+		Setting a value less than or equal to zero, disables updates.
 		</para>
 		<para>
 		<emphasis>Default value is <quote>100</quote>.
@@ -256,6 +259,27 @@ modparam("pua", "dlginfo_increase_version", 1)
 ...
 modparam("pua", "reginfo_increase_version", 1)
 ...
+</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("pua", "db_mode", 0)
+...
 </programlisting>
 		</example>
 	</section>

+ 68 - 5
modules_k/pua/hash.c

@@ -35,6 +35,7 @@
 #include "../../parser/parse_from.h"
 #include "hash.h" 
 #include "pua.h"
+#include "pua_db.h"
 #include "send_publish.h"
 
 void print_ua_pres(ua_pres_t* p)
@@ -62,8 +63,14 @@ htable_t* new_htable(void)
 {
 	htable_t* H= NULL;
 	int i= 0, j;
-
 	H= (htable_t*)shm_malloc(sizeof(htable_t));
+
+	if (dbmode==PUA_DB_ONLY)
+	{
+		LM_ERR( "new_htable shouldn't be called in PUA_DB_ONLY mode\n" );
+		return(NULL);
+	}
+
 	if(H== NULL)
 	{
 		LM_ERR("No more memory\n");
@@ -116,8 +123,14 @@ error:
 ua_pres_t* search_htable(ua_pres_t* pres, unsigned int hash_code)
 {
 	ua_pres_t* p= NULL,* L= NULL;
-
 	L= HashT->p_records[hash_code].entity;
+
+	if (dbmode==PUA_DB_ONLY)
+	{
+		LM_ERR( "search_htable shouldn't be called in PUA_DB_ONLY mode\n" );
+		return(NULL);
+	}
+
 	LM_DBG("core_hash= %u\n", hash_code);
 
 	for(p= L->next; p; p=p->next)
@@ -179,6 +192,13 @@ ua_pres_t* search_htable(ua_pres_t* pres, unsigned int hash_code)
 void update_htable(ua_pres_t* p, time_t desired_expires, int expires,
 		str* etag, unsigned int hash_code, str* contact)
 {
+
+	if (dbmode==PUA_DB_ONLY)
+	{
+		LM_ERR( "update_htable shouldn't be called in PUA_DB_ONLY mode\n" );
+		return;
+	}
+
 	if(etag)
 	{	
 		shm_free(p->etag.s);
@@ -220,6 +240,12 @@ void insert_htable(ua_pres_t* presentity)
 	ua_pres_t* p= NULL;
 	unsigned int hash_code;
 
+	if (dbmode==PUA_DB_ONLY)
+	{
+		LM_ERR( "insert_htable shouldn't be called in PUA_DB_ONLY mode\n" );
+		return;
+	}
+
 	hash_code= core_hash(presentity->pres_uri,presentity->watcher_uri, 
 			HASH_SIZE);
 	
@@ -252,6 +278,12 @@ void delete_htable(ua_pres_t* presentity, unsigned int hash_code)
 { 
 	ua_pres_t *q = NULL;
 
+	if (dbmode==PUA_DB_ONLY)
+	{
+		LM_ERR( "delete_htable shouldn't be called in PUA_DB_ONLY mode\n" );
+		return;
+	}
+
 	if (presentity == NULL)
 		return;
 
@@ -277,6 +309,12 @@ void destroy_htable(void)
 	ua_pres_t* p= NULL,*q= NULL;
 	int i;
 
+	if (dbmode==PUA_DB_ONLY)
+	{
+		LM_ERR( "destroy_htable shouldn't be called in PUA_DB_ONLY mode\n" );
+		return;
+	}
+
 	for(i=0; i<HASH_SIZE; i++)
 	{	
 		lock_destroy(&HashT->p_records[i].lock);
@@ -306,6 +344,13 @@ void destroy_htable(void)
 ua_pres_t* get_dialog(ua_pres_t* dialog, unsigned int hash_code)
 {
 	ua_pres_t* p= NULL, *L;
+
+	if (dbmode==PUA_DB_ONLY)
+	{
+		LM_ERR( "get_dialog shouldn't be called in PUA_DB_ONLY mode\n" );
+		return(NULL);
+	}
+
 	LM_DBG("core_hash= %u\n", hash_code);
 
 	L= HashT->p_records[hash_code].entity;
@@ -380,6 +425,11 @@ int get_record_id(ua_pres_t* dialog, str** rec_id)
 	ua_pres_t* rec;
 	str* id;
 
+	if (dbmode==PUA_DB_ONLY)
+	{
+		return( get_record_id_puadb( dialog, rec_id ) );
+	}
+
 	*rec_id= NULL;
 
 	hash_code= core_hash(dialog->pres_uri, dialog->watcher_uri, HASH_SIZE);
@@ -428,7 +478,12 @@ int is_dialog(ua_pres_t* dialog)
 {
 	int ret_code= 0;
 	unsigned int hash_code;
-	
+
+	if (dbmode==PUA_DB_ONLY)
+	{
+		return( is_dialog_puadb(dialog) );
+	}	
+
 	hash_code= core_hash(dialog->pres_uri, dialog->watcher_uri, HASH_SIZE);
 	lock_get(&HashT->p_records[hash_code].lock);
 
@@ -517,8 +572,6 @@ int update_contact(struct sip_msg* msg, char* str1, char* str2)
 	hentity.to_tag= pto->tag_value;
 	hentity.from_tag= pfrom->tag_value;
 	
-	hash_code= core_hash(hentity.pres_uri,hentity.watcher_uri,
-				HASH_SIZE);
 
 	/* extract the contact */
 	if(msg->contact== NULL || msg->contact->body.s== NULL)
@@ -528,6 +581,16 @@ int update_contact(struct sip_msg* msg, char* str1, char* str2)
 	}
 	contact= msg->contact->body;
 
+	if (dbmode==PUA_DB_ONLY)
+	{
+		update_contact_puadb(&hentity, &contact );
+		free_to_params(&TO);
+		return(1);
+	}
+
+	hash_code= core_hash(hentity.pres_uri,hentity.watcher_uri,
+				HASH_SIZE);
+
 	lock_get(&HashT->p_records[hash_code].lock);
 
 	p= get_dialog(&hentity, hash_code);

+ 4 - 4
modules_k/pua/hash.h

@@ -80,8 +80,8 @@ typedef struct ua_pres{
 	/* publish */
 	str etag;
 	str tuple_id;
-	str* body;
-	str content_type;
+	str* body;        /* croc-not stored in db_only mode */
+	str content_type; /* croc-not stored in db_only mode */
 
 	/* subscribe */
 	str* watcher_uri;
@@ -90,8 +90,8 @@ typedef struct ua_pres{
     str from_tag;
 	int cseq;
 	int version;
-    int watcher_count;
-	str* outbound_proxy;
+/*  int watcher_count; croc-nolonger used!! */
+	str* outbound_proxy; /* croc-not sored in db_only mode */
 	str* extra_headers;
 	str record_route;
 	str remote_contact;

+ 52 - 19
modules_k/pua/pua.c

@@ -70,6 +70,7 @@ int startup_time = 0;
 int dlginfo_increase_version = 0;
 int reginfo_increase_version = 0;
 pua_event_t* pua_evlist= NULL;
+int dbmode = 0;
 
 /* database connection */
 db1_con_t *pua_db = NULL;
@@ -101,7 +102,6 @@ static int mod_init(void);
 static int child_init(int);
 static void destroy(void);
 
-static int update_pua(ua_pres_t* p, unsigned int hash_code);
 static ua_pres_t* build_uppubl_cbparam(ua_pres_t* p);
 
 static int db_restore(void);
@@ -127,6 +127,7 @@ static param_export_t params[]={
 	{"dlginfo_increase_version",	 INT_PARAM, &dlginfo_increase_version},
 	{"reginfo_increase_version",	 INT_PARAM, &reginfo_increase_version},
 	{"check_remote_contact", INT_PARAM, &check_remote_contact	},
+	{ "db_mode",                INT_PARAM, &dbmode},
 	{0,							 0,			0            }
 };
 
@@ -170,6 +171,9 @@ static int mod_init(void)
 	LM_DBG("db_url=%s/%d/%p\n", ZSW(db_url.s), db_url.len, db_url.s);
 	db_table.len = db_table.s ? strlen(db_table.s) : 0;
 	
+	if (dbmode==PUA_DB_ONLY) 
+		LM_ERR( "DB ONLY MODE ACTIVE\n" );
+
 	/* binding to database module  */
 	if (db_bind_mod(&db_url, &pua_dbf))
 	{
@@ -194,24 +198,33 @@ static int mod_init(void)
 		return -1;
 	}
 
-	if(HASH_SIZE<=1)
-		HASH_SIZE= 512;
-	else
-		HASH_SIZE = 1<<HASH_SIZE;
+	if (dbmode != PUA_DB_ONLY)
+	{ 
+		if(HASH_SIZE<=1)
+			HASH_SIZE= 512;
+		else
+			HASH_SIZE = 1<<HASH_SIZE;
 
-	HashT= new_htable();
-	if(HashT== NULL)
-	{
-		LM_ERR("while creating new hash table\n");
-		return -1;
-	}
-	if(db_restore()< 0)
-	{
+		HashT= new_htable();
+		if(HashT== NULL)
+		{
+			LM_ERR("while creating new hash table\n");
+			return -1;
+		}
+  		if(db_restore()< 0)
+		{
 		LM_ERR("while restoring hash_table\n");
 		return -1;
+		}
+	} 
+
+	if (dbmode != PUA_DB_DEFAULT && dbmode != PUA_DB_ONLY)
+	{
+		dbmode = PUA_DB_DEFAULT;
+		LM_ERR( "Invalid dbmode-using default mode\n" );
 	}
 
-	if(update_period<=0)
+	if(update_period<0)
 	{
 		LM_ERR("wrong clean_period\n");
 		return -1;
@@ -241,10 +254,14 @@ static int mod_init(void)
 
 	startup_time = (int) time(NULL);
 	
-	register_timer(hashT_clean, 0, update_period- 5);
-
-	register_timer(db_update, 0, update_period);
+	if (update_period > 0) /* probably should check > 5 here!! -croc */
+		register_timer(hashT_clean, 0, update_period- 5);
 
+	if (dbmode != PUA_DB_ONLY) 
+	{        
+		if (update_period > 0) 
+			register_timer(db_update, 0, update_period);
+	}
 
 	if(pua_db)
 		pua_dbf.close(pua_db);
@@ -288,6 +305,8 @@ static void destroy(void)
 	if (puacb_list)
 		destroy_puacb_list();
 
+	/* if dbmode is PUA_DB_ONLY, then HashT will be NULL 
+		so db_update and destroy_htable won't get called */ 
 	if(pua_db && HashT)
 		db_update(0,0);
 	
@@ -321,6 +340,12 @@ static int db_restore(void)
 	int event_col,contact_col,tuple_col,record_route_col, extra_headers_col;
 	int version_col;
 
+	if (dbmode==PUA_DB_ONLY)
+	{
+		LM_ERR( "db_restore shouldn't be called in PUA_DB_ONLY mode\n" );
+		return(-1);
+	}
+
 	result_cols[puri_col=n_result_cols++]	= &str_pres_uri_col;
 	result_cols[pid_col=n_result_cols++]	= &str_pres_id_col;
 	result_cols[expires_col=n_result_cols++]= &str_expires_col;
@@ -595,6 +620,12 @@ static void hashT_clean(unsigned int ticks,void *param)
 	time_t now;
 	ua_pres_t* p= NULL, *q= NULL;
 
+	if (dbmode==PUA_DB_ONLY) 
+	{
+		clean_puadb(update_period, min_expires );
+		return;
+	}
+
 	now = time(NULL);
 	for(i= 0;i< HASH_SIZE; i++)
 	{
@@ -608,7 +639,7 @@ static void hashT_clean(unsigned int ticks,void *param)
 				if((p->desired_expires> p->expires + min_expires) || 
 						(p->desired_expires== 0 ))
 				{
-					if(update_pua(p, i)< 0)
+					if(update_pua(p)< 0)
 					{
 						LM_ERR("while updating record\n");
 						lock_release(&HashT->p_records[i].lock);
@@ -637,7 +668,7 @@ static void hashT_clean(unsigned int ticks,void *param)
 
 }
 
-int update_pua(ua_pres_t* p, unsigned int hash_code)
+int update_pua(ua_pres_t* p)
 {
 	str* str_hdr= NULL;
 	int expires;
@@ -757,6 +788,8 @@ static void db_update(unsigned int ticks,void *param)
 	int no_lock= 0, contact_col, desired_expires_col, extra_headers_col;
 	int remote_contact_col, version_col;
 
+	if (dbmode==PUA_DB_ONLY) return;
+
 	if(ticks== 0 && param == NULL)
 		no_lock= 1;
 

+ 8 - 0
modules_k/pua/pua.h

@@ -34,6 +34,10 @@
 #define REQ_OTHER  0
 #define REQ_ME     1
 
+#define PUA_DB_DEFAULT 0
+#define PUA_DB_MODE1_RESERVED 1
+#define PUA_DB_ONLY 2
+
 extern str default_domain;
 extern struct tm_binds tmb;
 extern htable_t* HashT;
@@ -43,7 +47,11 @@ extern int pua_ul_publish;
 extern int default_expires;
 extern str outbound_proxy;
 extern int check_remote_contact;
+extern int dbmode;
 
 int reginfo_increase_version;
 
+extern int update_pua(ua_pres_t* p);
+extern int clean_puadb( int update_period, int min_expires );
+
 #endif

+ 1657 - 0
modules_k/pua/pua_db.c

@@ -0,0 +1,1657 @@
+/*
+ * $Id$
+ *
+ * pua db - presence user agent 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 "pua.h"
+
+/* database connection */
+extern db1_con_t *pua_db;
+extern db_func_t pua_dbf;
+
+/* database colums */
+static str str_id_col = str_init( "id" );
+static str str_pres_uri_col = str_init("pres_uri");
+static str str_pres_id_col = str_init("pres_id");
+static str str_expires_col= str_init("expires");
+static str str_flag_col= str_init("flag");
+static str str_etag_col= str_init("etag");
+static str str_tuple_id_col= str_init("tuple_id");
+static str str_watcher_uri_col= str_init("watcher_uri");
+static str str_call_id_col= str_init("call_id");
+static str str_to_tag_col= str_init("to_tag");
+static str str_from_tag_col= str_init("from_tag");
+static str str_cseq_col= str_init("cseq");
+static str str_event_col= str_init("event");
+static str str_record_route_col= str_init("record_route");
+static str str_contact_col= str_init("contact");
+static str str_remote_contact_col= str_init("remote_contact");
+static str str_extra_headers_col= str_init("extra_headers");
+static str str_desired_expires_col= str_init("desired_expires");
+static str str_version_col = str_init("version");
+ 
+static int puadb_debug=0;
+
+/******************************************************************************/
+
+void pua_trace( char *func_name, char *text, ua_pres_t *pres )
+{
+  str *w=pres->watcher_uri;
+
+  if (!puadb_debug) return;	
+
+    /*LM_ERR( "%s %s pres_uri=%.*s watcher_uri=%.*s\n", */
+    LM_ERR( "%s %s pres_uri=%.*s pres_id=%.*s flag=%d event=%d watcher_uri=%.*s\n", 
+           func_name, text, pres->pres_uri->len, pres->pres_uri->s,
+           pres->id.len, pres->id.s, pres->flag, pres->event,
+           w?w->len:0, w?w->s:"" );
+} 
+
+/******************************************************************************/
+
+void pua_trace_int( char *func_name, char *text, int val, ua_pres_t *pres )
+{
+  str *w=pres->watcher_uri;
+
+  if (!puadb_debug) return;
+
+    /*LM_ERR( "%s %s %d pres_uri=%.*s watcher_uri=%.*s\n", */
+    LM_ERR( "%s %s %d pres_uri=%.*s pres_id=%.*s flag=%d event=%d watcher_uri=%.*s\n", 
+           func_name, text, val, pres->pres_uri->len, pres->pres_uri->s,
+          pres->id.len, pres->id.s, pres->flag, pres->event,
+            w?w->len:0, w?w->s:"" );
+} 
+
+/******************************************************************************/
+
+void pua_trace_string( char *func_name, char *text, const char *val, ua_pres_t *pres )
+{
+  str *w=pres->watcher_uri;
+
+  if (!puadb_debug) return;
+
+    /*LM_ERR( "%s %s %s pres_uri=%.*s watcher_uri=%.*s\n", */
+    LM_ERR( "%s %s %s pres_uri=%.*s pres_id=%.*s flag=%d event=%d watcher_uri=%.*s\n", 
+           func_name, text, "val", pres->pres_uri->len, pres->pres_uri->s,
+           pres->id.len, pres->id.s, pres->flag, pres->event,
+            w?w->len:0, w?w->s:"" );
+} 
+
+/******************************************************************************/
+
+void free_results_puadb( db1_res_t *res )
+
+{
+  if (res) 
+  {
+    pua_dbf.free_result(pua_db, res);
+    if (puadb_debug) LM_ERR( "free_results_puadb\n" );
+  }
+}
+
+/******************************************************************************/
+
+void extract_row( db_val_t *values, ua_pres_t *result )
+
+{
+
+  if (result->pres_uri != NULL )
+  {
+    result->pres_uri->s = (char *)VAL_STRING(values+1);
+    result->pres_uri->len = strlen(VAL_STRING(values+1));
+  }
+
+  result->id.s = (char *)VAL_STRING(values+2);
+  result->id.len = strlen(VAL_STRING(values+2));
+
+  result->event = VAL_INT(values+3);
+
+  result->expires = VAL_INT(values+4);
+ 
+  result->desired_expires = VAL_INT(values+5);
+
+  result->flag = VAL_INT(values+6);
+
+  /* publish */
+  result->etag.s = (char *)VAL_STRING(values+7);
+  result->etag.len = strlen(VAL_STRING(values+7));
+
+  result->tuple_id.s = (char *)VAL_STRING(values+8);
+  result->tuple_id.len = strlen(VAL_STRING(values+8));
+
+  /* subscribe */
+  if (result->watcher_uri != NULL )
+  {
+    result->watcher_uri->s = (char *)VAL_STRING(values+9);
+    result->watcher_uri->len = strlen(VAL_STRING(values+9));
+  }
+
+  result->call_id.s = (char *)VAL_STRING(values+10);
+  result->call_id.len = strlen(VAL_STRING(values+10));
+
+  result->to_tag.s = (char *)VAL_STRING(values+11);
+  result->to_tag.len = strlen(VAL_STRING(values+11));
+
+  result->from_tag.s = (char *)VAL_STRING(values+12);
+  result->from_tag.len = strlen(VAL_STRING(values+12));
+
+  result->cseq = VAL_INT(values+13);
+
+  result->record_route.s = (char *)VAL_STRING(values+14);
+  result->record_route.len = strlen(VAL_STRING(values+14));
+
+  result->contact.s = (char *)VAL_STRING(values+15);
+  result->contact.len = strlen(VAL_STRING(values+15));
+
+  result->remote_contact.s = (char *)VAL_STRING(values+16);
+  result->remote_contact.len = strlen(VAL_STRING(values+16));
+
+  result->version = VAL_INT(values+17);
+
+  if (result->extra_headers != NULL )
+  {
+    result->extra_headers->s = (char *)VAL_STRING(values+18);
+    result->extra_headers->len = strlen(VAL_STRING(values+18));
+  }
+
+  if (puadb_debug)
+  {
+    LM_ERR( "pres_uri is %s\n",VAL_STRING(values+1));
+    LM_ERR( "pres_id is %s\n",VAL_STRING(values+2));
+    LM_ERR( "etag is %s\n",VAL_STRING(values+7));
+    LM_ERR( "tuple_id is %s\n",VAL_STRING(values+8));
+    LM_ERR( "watcher uri is %s\n",VAL_STRING(values+9));
+  }
+
+}
+
+/******************************************************************************/
+
+int clean_puadb( int update_period, int min_expires )
+
+{
+  int i, nr_rows;
+  db_row_t *rows;
+  db_val_t *values;
+  db_key_t q_cols[1];
+  db1_res_t *res= NULL;
+  db_val_t q_vals[1];
+  db_op_t  q_ops[1];
+  int id;
+  time_t now;
+  ua_pres_t p;
+  str pres_uri={0,0}, watcher_uri={0,0}, extra_headers={0,0};
+
+  if (puadb_debug)
+    LM_ERR( "clean_puadb update_period=%d min expires=%d\n", update_period, min_expires );
+
+  memset(&p, 0, sizeof(p));
+  p.pres_uri = &pres_uri;
+  p.watcher_uri = &watcher_uri;
+  p.extra_headers = &extra_headers;
+
+  now = time(NULL);
+
+  /* cols and values used for search query */
+  q_cols[0] = &str_expires_col;
+  q_vals[0].type = DB1_INT;
+  q_vals[0].nul = 0;
+  q_vals[0].val.int_val = now+update_period;
+  q_ops[0] = OP_LT; 	
+
+  if(pua_dbf.query(pua_db, q_cols, q_ops, q_vals,
+                   NULL,1,0,0,&res) < 0)
+  {
+    LM_ERR("DB query error\n");
+    return(-1);
+  }
+
+  nr_rows = RES_ROW_N(res);
+
+  if (nr_rows == 0)
+  {
+    /* no match */ 
+    LM_INFO( "No records matched for clean\n");
+    pua_dbf.free_result(pua_db, res);
+    return(0);
+  }
+
+  /* get the results and update matching entries */
+  rows = RES_ROWS(res);
+
+  for (i=0; i < nr_rows; i++)
+  {
+    values = ROW_VALUES(rows+i);
+
+    extract_row( values, &p ); 
+    id = VAL_INT(values);
+
+    if((p.desired_expires> p.expires + min_expires) || (p.desired_expires== 0 ))
+    {
+/*    q_cols[0] = &str_id_col;
+      q_vals[0].type = DB1_INT;
+      q_vals[0].nul = 0;
+      q_vals[0].val.int_val = id;	
+
+      n_update_cols=0;
+      db_cols[n_update_cols] = &str_expires_col;
+      db_vals[n_update_cols].type = DB1_INT;
+      db_vals[n_update_cols].nul = 0; 
+      db_vals[n_update_cols].val.int_val = p.expires;
+      n_update_cols++;
+
+      db_cols[n_update_cols] = &str_desired_expires_col;
+      db_vals[n_update_cols].type = DB1_INT;
+      db_vals[n_update_cols].nul = 0; 
+      db_vals[n_update_cols].val.int_val = p.desired_expires;
+      n_update_cols++;
+
+      if(pua_dbf.update(pua_db, q_cols, 0, q_vals,
+                        db_cols,db_vals,1,n_update_cols) < 0)
+      {
+        LM_ERR( "update of db FAILED\n" );
+      }*/
+
+      if(update_pua(&p)< 0)
+      {
+	LM_ERR("update_pua failed\n");
+        break; /* >>>>>>> do we really want to do this here*/
+      }
+      continue;
+    }
+	
+    if(p.expires < now - 10)
+    {
+      LM_DBG("Found expired: uri= %.*s\n", p.pres_uri->len, p.pres_uri->s);
+      q_cols[0] = &str_id_col;
+      q_vals[0].type = DB1_INT;
+      q_vals[0].nul = 0;
+      q_vals[0].val.int_val = id;
+
+      if ( pua_dbf.delete(pua_db, q_cols, 0, q_vals, 1) < 0 )
+      {
+         LM_ERR( "Failed to delete from db\n" ); 
+      }			
+    }
+						
+  } /* for i */
+
+  pua_dbf.free_result(pua_db, res);
+  if (puadb_debug) LM_ERR( "Done\n" );
+  return(0);
+}
+
+/******************************************************************************/
+
+int matches_in_puadb(ua_pres_t *pres) 
+
+{
+  int nr_rows;
+  db_key_t q_cols[20];
+  db1_res_t *res= NULL;
+  db_val_t q_vals[20];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0;
+  int puri_col,pid_col,flag_col,etag_col, event_col;
+  int watcher_col;
+  int remote_contact_col;	
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(-1);
+  }
+
+  /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+	
+  q_cols[pid_col= n_query_cols] = &str_pres_id_col;	
+  q_vals[pid_col].type = DB1_STR;
+  q_vals[pid_col].nul = 0;
+  q_vals[pid_col].val.str_val.s = pres->id.s;
+  q_vals[pid_col].val.str_val.len = pres->id.len;
+  q_ops[pid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[flag_col= n_query_cols] = &str_flag_col;
+  q_vals[flag_col].type = DB1_INT;
+  q_vals[flag_col].nul = 0;
+  q_vals[flag_col].val.int_val = pres->flag;
+  q_ops[flag_col] = OP_BITWISE_AND;
+  n_query_cols++;
+
+  q_cols[event_col= n_query_cols] = &str_event_col;
+  q_vals[event_col].type = DB1_INT;
+  q_vals[event_col].nul = 0;
+  q_vals[event_col].val.int_val = pres->event;
+  q_ops[event_col] = OP_BITWISE_AND;
+  n_query_cols++;
+
+
+  if(pres->watcher_uri)
+  {
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+
+    if (check_remote_contact != 0)
+    {
+      q_cols[remote_contact_col= n_query_cols] = &str_remote_contact_col;
+      q_vals[remote_contact_col].type = DB1_STR;
+      q_vals[remote_contact_col].nul = 0;
+      q_vals[remote_contact_col].val.str_val.s = pres->remote_contact.s;
+      q_vals[remote_contact_col].val.str_val.len = pres->remote_contact.len;
+      q_ops[remote_contact_col] = OP_EQ;
+      n_query_cols++;
+    }
+
+  }
+  else /* no watcher _uri */
+  {
+
+    if(pres->etag.s)
+    {
+      q_cols[etag_col= n_query_cols] = &str_etag_col;
+      q_vals[etag_col].type = DB1_STR;
+      q_vals[etag_col].nul = 0;
+      q_vals[etag_col].val.str_val.s = pres->etag.s;
+      q_vals[etag_col].val.str_val.len = pres->etag.len;
+      q_ops[etag_col] = OP_EQ;
+      n_query_cols++;						
+    }
+
+  }
+
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(-1);
+  }
+
+  if(pua_dbf.query(pua_db, q_cols, q_ops, q_vals,
+                                         0,n_query_cols,0,0,&res) < 0)
+  {
+    LM_ERR("DB query error\n");
+    return(-1);
+  }
+
+  nr_rows = RES_ROW_N(res);
+
+  pua_dbf.free_result(pua_db, res);
+  pua_trace_int( "matches_in_puadb", "DONE rval=", nr_rows, pres );
+  return(nr_rows);
+}
+
+/******************************************************************************/
+
+ua_pres_t* search_puadb(ua_pres_t *pres, ua_pres_t *result, db1_res_t **dbres) 
+
+{
+  int nr_rows;
+  db_row_t *rows;
+  db_key_t q_cols[20];
+  db1_res_t *res= NULL;
+  db_val_t q_vals[20];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0;
+  int puri_col,pid_col,flag_col,etag_col,event_col;
+  int watcher_col;
+  int remote_contact_col;	
+  db_val_t *values;
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(NULL);
+  }
+
+  *dbres = NULL;
+
+  if (result==NULL) { LM_ERR( "result is NULL\n" ); return(NULL); }		
+
+  /* cols and values used for search query */ 
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+	
+  q_cols[pid_col= n_query_cols] = &str_pres_id_col;	
+  q_vals[pid_col].type = DB1_STR;
+  q_vals[pid_col].nul = 0;
+  q_vals[pid_col].val.str_val.s = pres->id.s;
+  q_vals[pid_col].val.str_val.len = pres->id.len;
+  q_ops[pid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[flag_col= n_query_cols] = &str_flag_col;
+  q_vals[flag_col].type = DB1_INT;
+  q_vals[flag_col].nul = 0;
+  q_vals[flag_col].val.int_val = pres->flag;
+  q_ops[flag_col] = OP_BITWISE_AND;
+  n_query_cols++;
+
+  q_cols[event_col= n_query_cols] = &str_event_col;
+  q_vals[event_col].type = DB1_INT;
+  q_vals[event_col].nul = 0;
+  q_vals[event_col].val.int_val = pres->event;
+  q_ops[event_col] = OP_BITWISE_AND;
+  n_query_cols++;
+
+  if(pres->watcher_uri)
+  {
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+
+    if (check_remote_contact != 0)
+    {
+      q_cols[remote_contact_col= n_query_cols] = &str_remote_contact_col;
+      q_vals[remote_contact_col].type = DB1_STR;
+      q_vals[remote_contact_col].nul = 0;
+      q_vals[remote_contact_col].val.str_val.s = pres->remote_contact.s;
+      q_vals[remote_contact_col].val.str_val.len = pres->remote_contact.len;
+      q_ops[remote_contact_col] = OP_EQ;
+      n_query_cols++;
+    }
+
+  }
+  else /* no watcher _uri */
+  {
+
+    if(pres->etag.s)
+    {
+      q_cols[etag_col= n_query_cols] = &str_etag_col;
+      q_vals[etag_col].type = DB1_STR;
+      q_vals[etag_col].nul = 0;
+      q_vals[etag_col].val.str_val.s = pres->etag.s;
+      q_vals[etag_col].val.str_val.len = pres->etag.len;
+      q_ops[etag_col] = OP_EQ;
+      n_query_cols++;						
+    }
+
+  }
+
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(NULL);
+  }
+
+  if(pua_dbf.query(pua_db, q_cols, q_ops, q_vals,
+                   NULL,n_query_cols,0,0,&res) < 0)
+  {
+    LM_ERR("DB query error\n");
+    return(NULL);
+  }
+
+  nr_rows = RES_ROW_N(res);
+
+  if (nr_rows == 0)
+  {
+    /* no match */ 
+    pua_trace( "search_puadb", "NO MATCH", pres );
+    pua_dbf.free_result(pua_db, res);
+    return(NULL);
+  }
+
+  if (nr_rows != 1)
+  {
+    pua_trace_int( "search_puadb", "TOO MANY MATCHES=", nr_rows, pres );   
+    pua_dbf.free_result(pua_db, res);
+    return(NULL);
+  }
+
+
+  /* get the results and fill in return data structure */
+  rows = RES_ROWS(res);
+  values = ROW_VALUES(rows);
+
+  extract_row( values, result );
+ 
+  /*pua_dbf.free_result(pua_db, res);*/
+  *dbres = res;
+
+  pua_trace( "search_puadb", "DONE", pres );  
+
+  return(result);
+}
+
+/******************************************************************************/
+
+ua_pres_t* get_dialog_puadb(ua_pres_t *pres, ua_pres_t *result, db1_res_t **dbres) 
+
+{
+  int nr_rows;
+  db_row_t *rows;
+  db_key_t q_cols[20];
+  db1_res_t *res= NULL;
+  db_val_t q_vals[20];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0;
+  int puri_col,callid_col;
+  int watcher_col, totag_col, fromtag_col, flag_col;	
+  db_val_t *values;
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(NULL);
+  }
+
+  *dbres = NULL;
+
+  if (result==NULL) { LM_ERR( "result is NULL\n" ); return(NULL); }
+
+  /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+
+  q_cols[flag_col= n_query_cols] = &str_flag_col;
+  q_vals[flag_col].type = DB1_INT;
+  q_vals[flag_col].nul = 0;
+  q_vals[flag_col].val.int_val = pres->flag;
+  q_ops[flag_col] = OP_BITWISE_AND;
+  n_query_cols++;
+	
+  if (pres->watcher_uri)
+  {
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+  }
+  else
+  {
+    LM_ERR( "get_dialog_puadb has NULL watcher_uri\n" );
+  }
+
+  q_cols[callid_col= n_query_cols] = &str_call_id_col;	
+  q_vals[callid_col].type = DB1_STR;
+  q_vals[callid_col].nul = 0;
+  q_vals[callid_col].val.str_val.s = pres->call_id.s;
+  q_vals[callid_col].val.str_val.len = pres->call_id.len;
+  q_ops[callid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[totag_col= n_query_cols] = &str_to_tag_col;	
+  q_vals[totag_col].type = DB1_STR;
+  q_vals[totag_col].nul = 0;
+  q_vals[totag_col].val.str_val.s = pres->to_tag.s;
+  q_vals[totag_col].val.str_val.len = pres->to_tag.len;
+  q_ops[totag_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[fromtag_col= n_query_cols] = &str_from_tag_col;	
+  q_vals[fromtag_col].type = DB1_STR;
+  q_vals[fromtag_col].nul = 0;
+  q_vals[fromtag_col].val.str_val.s = pres->from_tag.s;
+  q_vals[fromtag_col].val.str_val.len = pres->from_tag.len;
+  q_ops[fromtag_col] = OP_EQ;
+  n_query_cols++;
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(NULL);
+  }
+
+  if(pua_dbf.query(pua_db, q_cols, q_ops, q_vals,
+                                         0,n_query_cols,0,0,&res) < 0)
+  {
+    LM_ERR("DB query error\n");
+    return(NULL);
+  }
+
+  nr_rows = RES_ROW_N(res);
+
+  if (nr_rows == 0)
+  {
+    /* no match */ 
+    pua_trace( "get_dialog_puadb", "NO MATCH", pres );  
+    pua_dbf.free_result(pua_db, res);
+    return(NULL);
+  }
+
+  if (nr_rows != 1)
+  {
+    pua_trace_int( "get_dialog_puadb", "TOO MANY RESULTS=", nr_rows, pres );  
+    return(NULL);
+  }
+
+
+  /* get the results and fill in return data structure */
+  rows = RES_ROWS(res);
+  values = ROW_VALUES(rows);
+ 
+  extract_row( values, result );
+ 
+  /*pua_dbf.free_result(pua_db, res);*/
+  *dbres = res;
+
+  pua_trace( "get_dialog_puadb", "DONE", pres );  
+  return(result);
+}
+
+/******************************************************************************/
+
+int is_dialog_puadb(ua_pres_t *pres) 
+
+{
+  int nr_rows;
+  db_key_t q_cols[20], res_cols[5];
+  db1_res_t *res= NULL;
+  db_val_t q_vals[20];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0, n_res_cols=0;
+  int puri_col,callid_col;
+  int watcher_col, totag_col, fromtag_col;	
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(-1);
+  }
+
+  /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+
+  if (pres->watcher_uri)
+  {	
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+  }
+  else
+  {
+    LM_ERR( "is_dialog_puadb has NULL watcher_uri\n" );
+  }
+
+
+  q_cols[callid_col= n_query_cols] = &str_call_id_col;	
+  q_vals[callid_col].type = DB1_STR;
+  q_vals[callid_col].nul = 0;
+  q_vals[callid_col].val.str_val.s = pres->call_id.s;
+  q_vals[callid_col].val.str_val.len = pres->call_id.len;
+  q_ops[callid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[totag_col= n_query_cols] = &str_to_tag_col;	
+  q_vals[totag_col].type = DB1_STR;
+  q_vals[totag_col].nul = 0;
+  q_vals[totag_col].val.str_val.s = pres->to_tag.s;
+  q_vals[totag_col].val.str_val.len = pres->to_tag.len;
+  q_ops[totag_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[fromtag_col= n_query_cols] = &str_from_tag_col;	
+  q_vals[fromtag_col].type = DB1_STR;
+  q_vals[fromtag_col].nul = 0;
+  q_vals[fromtag_col].val.str_val.s = pres->from_tag.s;
+  q_vals[fromtag_col].val.str_val.len = pres->from_tag.len;
+  q_ops[fromtag_col] = OP_EQ;
+  n_query_cols++;
+
+  /* return the id column, even though don't actually need */
+  res_cols[n_res_cols] = &str_id_col;	
+  n_res_cols++;
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(-1);
+  }
+
+
+  if(pua_dbf.query(pua_db, q_cols, q_ops, q_vals,
+                                         res_cols,n_query_cols,n_res_cols,0,&res) < 0)
+  {
+    LM_ERR("DB query error\n");
+    return(-1);
+  }
+
+  nr_rows = RES_ROW_N(res);
+  pua_dbf.free_result(pua_db, res);
+
+  if (nr_rows == 0)
+  {
+    /* no match */ 
+    pua_trace( "is_dialog_puadb", "NO MATCH", pres );  
+    return(-1);
+  }
+
+  if (nr_rows != 1)
+  {
+    pua_trace_int( "is_dialog_puadb", "TOO MANY RESULTS=", nr_rows, pres );
+    return(-1);
+  }
+
+
+  pua_trace( "is_dialog_puadb", "MATCH", pres );
+  return(0);
+}
+
+/******************************************************************************/
+
+int get_record_id_puadb(ua_pres_t *pres, str **rec_id ) 
+
+{
+  int nr_rows;
+  db_row_t *rows;
+  db_key_t q_cols[20], res_cols[5];
+  db1_res_t *res= NULL;
+  db_val_t q_vals[20];
+  db_op_t  q_ops[20];
+  int n_query_cols=0, n_res_cols=0;
+  int puri_col,callid_col;
+  int watcher_col, totag_col, fromtag_col;
+  int pres_id_col;	
+  db_val_t *values;	
+  str *id;	
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(-1);
+  }
+ 
+ /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+
+  if (pres->watcher_uri) 
+  {
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+  }
+  else
+  {
+    LM_ERR( "get_record_id_puadb has NULL watcher_uri\n" );
+  }
+
+  q_cols[callid_col= n_query_cols] = &str_call_id_col;	
+  q_vals[callid_col].type = DB1_STR;
+  q_vals[callid_col].nul = 0;
+  q_vals[callid_col].val.str_val.s = pres->call_id.s;
+  q_vals[callid_col].val.str_val.len = pres->call_id.len;
+  q_ops[callid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[totag_col= n_query_cols] = &str_to_tag_col;	
+  q_vals[totag_col].type = DB1_STR;
+  q_vals[totag_col].nul = 0;
+  q_vals[totag_col].val.str_val.s = pres->to_tag.s;
+  q_vals[totag_col].val.str_val.len = pres->to_tag.len;
+  q_ops[totag_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[fromtag_col= n_query_cols] = &str_from_tag_col;	
+  q_vals[fromtag_col].type = DB1_STR;
+  q_vals[fromtag_col].nul = 0;
+  q_vals[fromtag_col].val.str_val.s = pres->from_tag.s;
+  q_vals[fromtag_col].val.str_val.len = pres->from_tag.len;
+  q_ops[fromtag_col] = OP_EQ;
+  n_query_cols++;
+
+  res_cols[n_res_cols] = &str_id_col;	
+  n_res_cols++;
+
+  res_cols[pres_id_col= n_res_cols] = &str_pres_id_col;	
+  n_res_cols++;
+
+  *rec_id = NULL;
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(-1);
+  }
+
+  if(pua_dbf.query(pua_db, q_cols, q_ops, q_vals,
+                                         res_cols,n_query_cols,n_res_cols,0,&res) < 0)
+  {
+    LM_ERR("DB query error\n");
+    return(-1);
+  }
+
+  nr_rows = RES_ROW_N(res);
+
+  if (nr_rows == 0)
+  {
+    /* no match */ 
+    pua_trace( "get_record_id_puadb", "NO MATCH", pres );
+    pua_dbf.free_result(pua_db, res);
+    return(0);
+  }
+
+  if (nr_rows != 1)
+  {
+    pua_trace_int( "get_record_id_puadb", "TOO MANY MATCHES=", nr_rows, pres );
+    pua_dbf.free_result(pua_db, res);
+    return(-1);
+  }
+
+
+  /* get the results and fill in return data structure */
+  rows = RES_ROWS(res);
+  values = ROW_VALUES(rows);
+
+  /* LM_ERR("db_id= %d\n", VAL_INT(values) ); */
+
+  id= (str*)pkg_malloc(sizeof(str));
+
+  if(id== NULL)
+  {
+    LM_ERR("No more memory\n");
+    pua_dbf.free_result(pua_db, res);
+    return(-1);
+  }
+
+  id->s= (char*)pkg_malloc( strlen(VAL_STRING(values+1)) * sizeof(char));
+
+  if(id->s== NULL)
+  {
+    LM_ERR("No more memory\n");
+    pkg_free(id);
+    pua_dbf.free_result(pua_db, res);
+    return(-1);
+  }
+  
+  memcpy(id->s, VAL_STRING(values+1), strlen(VAL_STRING(values+1)) );
+  id->len= strlen(VAL_STRING(values+1));
+
+  *rec_id= id;
+  pua_dbf.free_result(pua_db, res);
+
+  pua_trace_string( "get_record_id_puadb", "FOUND id=", VAL_STRING(values+1), pres );
+  return(0);
+}
+
+/******************************************************************************/
+
+int delete_temporary_dialog_puadb(ua_pres_t *pres ) 
+
+{
+  db_key_t q_cols[20];
+  db_val_t q_vals[20];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0;
+  int puri_col,callid_col;
+  int watcher_col, totag_col, fromtag_col;
+  int rval;	
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(-1);
+  }
+
+  /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+
+  if (pres->watcher_uri)
+  {	
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+  }
+  else
+  {
+    LM_ERR( "delete_temporary_dialog_puadb has NULL watcher_uri\n" );
+  }
+
+
+  q_cols[callid_col= n_query_cols] = &str_call_id_col;	
+  q_vals[callid_col].type = DB1_STR;
+  q_vals[callid_col].nul = 0;
+  q_vals[callid_col].val.str_val.s = pres->call_id.s;
+  q_vals[callid_col].val.str_val.len = pres->call_id.len;
+  q_ops[callid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[totag_col= n_query_cols] = &str_to_tag_col;	
+  q_vals[totag_col].type = DB1_STR;
+  q_vals[totag_col].nul = 0;
+  q_vals[totag_col].val.str_val.s = "";
+  q_vals[totag_col].val.str_val.len = 0;
+  q_ops[totag_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[fromtag_col= n_query_cols] = &str_from_tag_col;	
+  q_vals[fromtag_col].type = DB1_STR;
+  q_vals[fromtag_col].nul = 0;
+  q_vals[fromtag_col].val.str_val.s = pres->from_tag.s;
+  q_vals[fromtag_col].val.str_val.len = pres->from_tag.len;
+  q_ops[fromtag_col] = OP_EQ;
+  n_query_cols++;
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(-1);
+  }
+
+  rval = pua_dbf.delete(pua_db, q_cols, q_ops, q_vals, n_query_cols);
+
+  if ( rval < 0 ) 
+  {
+    pua_trace( "delete_temporary_dialog_puad", "NOT FOUND", pres );
+  }
+  else
+  {
+    pua_trace( "delete_temporary_dialog_puad", "DONE", pres );
+  }
+
+  return(rval);
+}
+
+/******************************************************************************/
+
+int delete_puadb(ua_pres_t *pres ) 
+
+{
+  db_key_t q_cols[20];
+  db_val_t q_vals[20];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0;
+  int puri_col,pid_col,flag_col,etag_col,event_col;
+  int watcher_col;
+  int remote_contact_col;	
+  int rval;		
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(-1);
+  }
+
+  /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+	
+  q_cols[pid_col= n_query_cols] = &str_pres_id_col;	
+  q_vals[pid_col].type = DB1_STR;
+  q_vals[pid_col].nul = 0;
+  q_vals[pid_col].val.str_val.s = pres->id.s;
+  q_vals[pid_col].val.str_val.len = pres->id.len;
+  q_ops[pid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[flag_col= n_query_cols] = &str_flag_col;
+  q_vals[flag_col].type = DB1_INT;
+  q_vals[flag_col].nul = 0;
+  q_vals[flag_col].val.int_val = pres->flag;
+  q_ops[flag_col] = OP_BITWISE_AND;
+  n_query_cols++;
+
+  q_cols[event_col= n_query_cols] = &str_event_col;
+  q_vals[event_col].type = DB1_INT;
+  q_vals[event_col].nul = 0;
+  q_vals[event_col].val.int_val = pres->event;
+  q_ops[event_col] = OP_BITWISE_AND;
+  n_query_cols++;
+
+
+  if(pres->watcher_uri)
+  {
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+
+    if (check_remote_contact != 0)
+    {
+      q_cols[remote_contact_col= n_query_cols] = &str_remote_contact_col;
+      q_vals[remote_contact_col].type = DB1_STR;
+      q_vals[remote_contact_col].nul = 0;
+      q_vals[remote_contact_col].val.str_val.s = pres->remote_contact.s;
+      q_vals[remote_contact_col].val.str_val.len = pres->remote_contact.len;
+      q_ops[remote_contact_col] = OP_EQ;
+      n_query_cols++;
+    }
+
+  }
+  else /* no watcher _uri */
+  {
+
+    if(pres->etag.s)
+    {
+      q_cols[etag_col= n_query_cols] = &str_etag_col;
+      q_vals[etag_col].type = DB1_STR;
+      q_vals[etag_col].nul = 0;
+      q_vals[etag_col].val.str_val.s = pres->etag.s;
+      q_vals[etag_col].val.str_val.len = pres->etag.len;
+      q_ops[etag_col] = OP_EQ;
+      n_query_cols++;						
+    }
+
+  }
+
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(-1);
+  }
+
+  rval = pua_dbf.delete(pua_db, q_cols, q_ops, q_vals, n_query_cols);
+
+  if ( rval < 0 ) 
+  {
+    pua_trace( "delete_puad", "NOT FOUND", pres );
+  }
+  else
+  {
+    pua_trace( "delete_puad", "DONE", pres );
+  }
+
+  return(rval);
+}
+
+/******************************************************************************/
+
+int update_contact_puadb(ua_pres_t *pres, str *contact) 
+
+{
+  db_key_t q_cols[20], db_cols[5];
+  db_val_t q_vals[20], db_vals[5];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0, n_update_cols=0;
+  int puri_col,callid_col;
+  int watcher_col, totag_col, fromtag_col;
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(-1);
+  }
+
+  /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+	
+  if (pres->watcher_uri)
+  {
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+  }
+  else
+  {
+    LM_ERR( "update_contact_puadb has NULL watcher_uri\n" );
+  }
+
+  q_cols[callid_col= n_query_cols] = &str_call_id_col;	
+  q_vals[callid_col].type = DB1_STR;
+  q_vals[callid_col].nul = 0;
+  q_vals[callid_col].val.str_val.s = pres->call_id.s;
+  q_vals[callid_col].val.str_val.len = pres->call_id.len;
+  q_ops[callid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[totag_col= n_query_cols] = &str_to_tag_col;	
+  q_vals[totag_col].type = DB1_STR;
+  q_vals[totag_col].nul = 0;
+  q_vals[totag_col].val.str_val.s = pres->to_tag.s;
+  q_vals[totag_col].val.str_val.len = pres->to_tag.len;
+  q_ops[totag_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[fromtag_col= n_query_cols] = &str_from_tag_col;	
+  q_vals[fromtag_col].type = DB1_STR;
+  q_vals[fromtag_col].nul = 0;
+  q_vals[fromtag_col].val.str_val.s = pres->from_tag.s;
+  q_vals[fromtag_col].val.str_val.len = pres->from_tag.len;
+  q_ops[fromtag_col] = OP_EQ;
+  n_query_cols++;
+
+
+  /* we overwrite contact even if not changed */
+  db_cols[n_update_cols] = &str_contact_col; /* had remote here, think was a bug */
+  db_vals[n_update_cols].type = DB1_STR;
+  db_vals[n_update_cols].nul = 0; 
+  db_vals[n_update_cols].val.str_val.s = contact->s;
+  db_vals[n_update_cols].val.str_val.len = contact->len;
+  n_update_cols++;
+ 
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(-1);
+  }
+
+  if(pua_dbf.update(pua_db, q_cols, q_ops, q_vals,
+                    db_cols,db_vals,n_query_cols,n_update_cols) < 0)
+  {
+    pua_trace( "update_contact_puad", "FAILED", pres );
+    return(-1);
+  }
+  
+  pua_trace( "update_contact_puad", "DONE", pres );
+  return(0);
+}
+
+
+/******************************************************************************/
+
+int update_version_puadb(ua_pres_t *pres, int version ) 
+
+{
+  db_key_t q_cols[20], db_cols[5];
+  db_val_t q_vals[20], db_vals[5];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0, n_update_cols=0;
+  int puri_col,callid_col;
+  int watcher_col, totag_col, fromtag_col;
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return(-1);
+  }
+
+  /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+
+  if (pres->watcher_uri)
+  {	
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+  }
+  else
+  {
+    LM_ERR( "update_version_puadb has NULL watcher_uri\n" );
+  }
+
+  q_cols[callid_col= n_query_cols] = &str_call_id_col;	
+  q_vals[callid_col].type = DB1_STR;
+  q_vals[callid_col].nul = 0;
+  q_vals[callid_col].val.str_val.s = pres->call_id.s;
+  q_vals[callid_col].val.str_val.len = pres->call_id.len;
+  q_ops[callid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[totag_col= n_query_cols] = &str_to_tag_col;	
+  q_vals[totag_col].type = DB1_STR;
+  q_vals[totag_col].nul = 0;
+  q_vals[totag_col].val.str_val.s = pres->to_tag.s;
+  q_vals[totag_col].val.str_val.len = pres->to_tag.len;
+  q_ops[totag_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[fromtag_col= n_query_cols] = &str_from_tag_col;	
+  q_vals[fromtag_col].type = DB1_STR;
+  q_vals[fromtag_col].nul = 0;
+  q_vals[fromtag_col].val.str_val.s = pres->from_tag.s;
+  q_vals[fromtag_col].val.str_val.len = pres->from_tag.len;
+  q_ops[fromtag_col] = OP_EQ;
+  n_query_cols++;
+
+
+  /* we overwrite contact even if not changed */
+  db_cols[n_update_cols] = &str_version_col;
+  db_vals[n_update_cols].type = DB1_INT;
+  db_vals[n_update_cols].val.int_val = version;
+  n_update_cols++;
+ 
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return(-1);
+  }
+
+  if(pua_dbf.update(pua_db, q_cols, q_ops, q_vals,
+                    db_cols,db_vals,n_query_cols,n_update_cols) < 0)
+  
+  {
+    pua_trace( "update_version_puad", "FAILED", pres );
+    return(-1);
+  }
+  
+  pua_trace( "update_version_puad", "DONE", pres );
+  return(0);
+}
+
+
+/******************************************************************************/
+
+void update_puadb(ua_pres_t* pres, time_t desired_expires, 
+                       int expires, str* etag, str *contact)
+
+{
+  db_key_t q_cols[20];
+  db_key_t db_cols[5];
+  db_val_t q_vals[20], db_vals[5];
+  db_op_t  q_ops[20];
+  int n_query_cols= 0;
+  int n_update_cols= 0;
+  int puri_col,pid_col,flag_col,etag_col,event_col;
+  int watcher_col;
+  int remote_contact_col;	
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return;
+  }
+
+  /* cols and values used for search query */
+  q_cols[puri_col= n_query_cols] = &str_pres_uri_col;
+  q_vals[puri_col].type = DB1_STR;
+  q_vals[puri_col].nul = 0;
+  q_vals[puri_col].val.str_val.s = pres->pres_uri->s;
+  q_vals[puri_col].val.str_val.len = pres->pres_uri->len;
+  q_ops[puri_col] = OP_EQ; 
+  n_query_cols++;
+	
+  q_cols[pid_col= n_query_cols] = &str_pres_id_col;	
+  q_vals[pid_col].type = DB1_STR;
+  q_vals[pid_col].nul = 0;
+  q_vals[pid_col].val.str_val.s = pres->id.s;
+  q_vals[pid_col].val.str_val.len = pres->id.len;
+  q_ops[pid_col] = OP_EQ;
+  n_query_cols++;
+
+  q_cols[flag_col= n_query_cols] = &str_flag_col;
+  q_vals[flag_col].type = DB1_INT;
+  q_vals[flag_col].nul = 0;
+  q_vals[flag_col].val.int_val = pres->flag;
+  q_ops[flag_col] = OP_BITWISE_AND;
+  n_query_cols++;
+
+  q_cols[event_col= n_query_cols] = &str_event_col;
+  q_vals[event_col].type = DB1_INT;
+  q_vals[event_col].nul = 0;
+  q_vals[event_col].val.int_val = pres->event;
+  q_ops[event_col] = OP_BITWISE_AND;
+  n_query_cols++;
+
+
+  if(pres->watcher_uri)
+  {
+    q_cols[watcher_col= n_query_cols] = &str_watcher_uri_col;
+    q_vals[watcher_col].type = DB1_STR;
+    q_vals[watcher_col].nul = 0;
+    q_vals[watcher_col].val.str_val.s = pres->watcher_uri->s;
+    q_vals[watcher_col].val.str_val.len = pres->watcher_uri->len;
+    q_ops[watcher_col] = OP_EQ;
+    n_query_cols++;
+
+    if (check_remote_contact != 0)
+    {
+      q_cols[remote_contact_col= n_query_cols] = &str_remote_contact_col;
+      q_vals[remote_contact_col].type = DB1_STR;
+      q_vals[remote_contact_col].nul = 0;
+      q_vals[remote_contact_col].val.str_val.s = pres->remote_contact.s;
+      q_vals[remote_contact_col].val.str_val.len = pres->remote_contact.len;
+      q_ops[remote_contact_col] = OP_EQ;
+      n_query_cols++;
+    }
+
+  }
+  else /* no watcher _uri */
+  {
+
+    if(pres->etag.s)
+    {
+      q_cols[etag_col= n_query_cols] = &str_etag_col;
+      q_vals[etag_col].type = DB1_STR;
+      q_vals[etag_col].nul = 0;
+      q_vals[etag_col].val.str_val.s = pres->etag.s;
+      q_vals[etag_col].val.str_val.len = pres->etag.len;
+      q_ops[etag_col] = OP_EQ;
+      n_query_cols++;						
+    }
+
+  }
+
+  db_cols[n_update_cols] = &str_expires_col;
+  db_vals[n_update_cols].type = DB1_INT;
+  db_vals[n_update_cols].nul = 0; 
+  db_vals[n_update_cols].val.int_val = expires + (int)time(NULL);
+  n_update_cols++;
+
+  db_cols[n_update_cols] = &str_desired_expires_col;
+  db_vals[n_update_cols].type = DB1_INT;
+  db_vals[n_update_cols].nul = 0; 
+  db_vals[n_update_cols].val.int_val = desired_expires;
+  n_update_cols++;
+
+  if(pres->watcher_uri)
+  {
+    db_cols[n_update_cols] = &str_cseq_col;
+    db_vals[n_update_cols].type = DB1_INT;
+    db_vals[n_update_cols].nul = 0; 
+    db_vals[n_update_cols].val.int_val = pres->cseq+1;
+    n_update_cols++;
+  }
+  
+  if(etag)
+  {	
+    db_cols[n_update_cols] = &str_etag_col;
+    db_vals[n_update_cols].type = DB1_STR;
+    db_vals[n_update_cols].nul = 0; 
+    db_vals[n_update_cols].val.str_val.s = etag->s;
+    db_vals[n_update_cols].val.str_val.len = etag->len;
+    n_update_cols++;
+  }
+
+  if (contact)
+  {
+    /* we overwrite contact even if not changed,
+       saves retrieving record to check, when the 
+       record has gotta be updated anyway! */
+    db_cols[n_update_cols] = &str_contact_col; /* had remote here think was a bug */
+    db_vals[n_update_cols].type = DB1_STR;
+    db_vals[n_update_cols].nul = 0; 
+    db_vals[n_update_cols].val.str_val.s = contact->s;
+    db_vals[n_update_cols].val.str_val.len = contact->len;
+    n_update_cols++;
+  }
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return;
+  }
+
+  if(pua_dbf.update(pua_db, q_cols, q_ops, q_vals,
+                    db_cols,db_vals,n_query_cols,n_update_cols) < 0)
+  {
+    pua_trace( "update_puad", "FAILED", pres );
+  }
+  else
+  {
+    pua_trace( "update_puad", "DONE", pres );
+  }	
+
+}
+
+/******************************************************************************/
+
+void insert_puadb(ua_pres_t* pres)
+
+{
+  db_key_t db_cols[20];
+  db_val_t db_vals[20];
+  int n_cols= 0;
+
+  if (pres==NULL)
+  {
+    LM_ERR("called with NULL param\n");
+    return;
+  }
+
+  if (pres->pres_uri)
+  {
+    db_cols[n_cols] = &str_pres_uri_col;
+    db_vals[n_cols].type = DB1_STR;
+    db_vals[n_cols].nul = 0;
+    db_vals[n_cols].val.str_val.s = pres->pres_uri->s;
+    db_vals[n_cols].val.str_val.len = pres->pres_uri->len;
+    n_cols++;
+  }
+  else
+  {
+    LM_ERR( "insert_puadb called with NULL pres_uri\n" );
+  }
+	
+  db_cols[n_cols] = &str_pres_id_col;	
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0;
+  db_vals[n_cols].val.str_val.s = pres->id.s;
+  db_vals[n_cols].val.str_val.len = pres->id.len;
+  n_cols++;
+
+  db_cols[n_cols] = &str_event_col;	
+  db_vals[n_cols].type = DB1_INT;
+  db_vals[n_cols].nul = 0;
+  db_vals[n_cols].val.int_val = pres->event;
+  n_cols++;
+
+  db_cols[n_cols] = &str_expires_col;
+  db_vals[n_cols].type = DB1_INT;
+  db_vals[n_cols].nul = 0;
+  db_vals[n_cols].val.int_val = pres->expires;
+  n_cols++;
+
+  db_cols[n_cols] = &str_desired_expires_col;
+  db_vals[n_cols].type = DB1_INT;
+  db_vals[n_cols].nul = 0;
+  db_vals[n_cols].val.int_val = pres->desired_expires;
+  n_cols++;
+
+  db_cols[n_cols] = &str_flag_col;
+  db_vals[n_cols].type = DB1_INT;
+  db_vals[n_cols].nul = 0;
+  db_vals[n_cols].val.int_val = pres->flag;
+  n_cols++;
+
+
+  /* publish */
+  db_cols[n_cols] = &str_etag_col;
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0; 
+  db_vals[n_cols].val.str_val.s = pres->etag.s;
+  db_vals[n_cols].val.str_val.len = pres->etag.len;
+  n_cols++;
+
+  db_cols[n_cols] = &str_tuple_id_col;
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0; 
+  db_vals[n_cols].val.str_val.s = pres->tuple_id.s;
+  db_vals[n_cols].val.str_val.len = pres->tuple_id.len;
+  n_cols++;
+
+  /* subscribe */
+  if (pres->watcher_uri)
+  {
+    db_cols[n_cols] = &str_watcher_uri_col;
+    db_vals[n_cols].type = DB1_STR;
+    db_vals[n_cols].nul = 0; 
+    db_vals[n_cols].val.str_val.s = pres->watcher_uri->s;
+    db_vals[n_cols].val.str_val.len = pres->watcher_uri->len;
+    n_cols++;
+  }
+
+  db_cols[n_cols] = &str_call_id_col;
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0; 
+  db_vals[n_cols].val.str_val.s = pres->call_id.s;
+  db_vals[n_cols].val.str_val.len = pres->call_id.len;
+  n_cols++;
+
+  db_cols[n_cols] = &str_to_tag_col;
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0; 
+  db_vals[n_cols].val.str_val.s = pres->to_tag.s;
+  db_vals[n_cols].val.str_val.len = pres->to_tag.len;
+  n_cols++;
+
+  db_cols[n_cols] = &str_from_tag_col;
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0; 
+  db_vals[n_cols].val.str_val.s = pres->from_tag.s;
+  db_vals[n_cols].val.str_val.len = pres->from_tag.len;
+  n_cols++;
+
+  db_cols[n_cols] = &str_cseq_col;
+  db_vals[n_cols].type = DB1_INT;
+  db_vals[n_cols].nul = 0;
+  db_vals[n_cols].val.int_val = pres->cseq;
+  n_cols++;
+
+  db_cols[n_cols] = &str_record_route_col;
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0; 
+  db_vals[n_cols].val.str_val.s = pres->record_route.s;
+  db_vals[n_cols].val.str_val.len = pres->record_route.len;
+  n_cols++;
+
+  db_cols[n_cols] = &str_contact_col;
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0; 
+  db_vals[n_cols].val.str_val.s = pres->contact.s;
+  db_vals[n_cols].val.str_val.len = pres->contact.len;
+  n_cols++;
+
+  db_cols[n_cols] = &str_remote_contact_col;
+  db_vals[n_cols].type = DB1_STR;
+  db_vals[n_cols].nul = 0; 
+  db_vals[n_cols].val.str_val.s = pres->remote_contact.s;
+  db_vals[n_cols].val.str_val.len = pres->remote_contact.len;
+  n_cols++;
+
+  db_cols[n_cols] = &str_version_col;
+  db_vals[n_cols].type = DB1_INT;
+  db_vals[n_cols].nul = 0;
+  db_vals[n_cols].val.int_val = pres->version;
+  n_cols++;
+
+  if (pres->extra_headers)
+  {
+    db_cols[n_cols] = &str_extra_headers_col;
+    db_vals[n_cols].type = DB1_STR;
+    db_vals[n_cols].nul = 0; 
+    db_vals[n_cols].val.str_val.s = pres->extra_headers->s;
+    db_vals[n_cols].val.str_val.len = pres->extra_headers->len;
+    n_cols++;
+  }
+
+
+  if(pua_db == NULL)
+  {
+    LM_ERR("null database connection\n");
+    return;
+  }
+
+  if(pua_dbf.insert(pua_db, db_cols, db_vals, n_cols) < 0)  
+  {
+    pua_trace( "insert_puad", "FAILED", pres );
+  }
+  else
+  {
+    pua_trace( "insert_puad", "DONE", pres );  
+  }
+  
+}
+
+/******************************************************************************/
+

+ 64 - 0
modules_k/pua/pua_db.h

@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ *
+ * pua_db headers - presence user agent db headers
+ *
+ * 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
+ */
+
+#ifndef PUA_DB_H
+#define PUA_DB_H
+
+#include "../../lib/srdb1/db.h"
+
+#define PUA_PRES_URI (1<<0) 
+#define PUA_PRES_ID (1<<1)
+#define PUA_EVENT (1<<2)
+#define PUA_EXPIRES (1<<3)
+#define PUA_DESIRED_EXPIRES (1<<4)
+#define PUA_FLAG (1<<5)
+#define PUA_ETAG (1<<6)
+#define PUA_TUPLE_ID (1<<7)
+#define PUA_WATCHER_URI (1<<8)
+#define PUA_CALL_ID (1<<9)
+#define PUA_TO_TAG (1<<10)
+#define PUA_FROM_TAG (1<<11)
+#define PUA_CSEQ  (1<<12)
+#define PUA_RECORD_ROUTE (1<<13)
+#define PUA_CONTACT (1<<14)
+#define PUA_REMOTE_CONTACT (1<<15)
+#define PUA_VERSION (1<<16)
+#define PUA_EXTRA_HEADERS (1<<17)
+
+void free_results_puadb( db1_res_t *res );
+int matches_in_puadb(ua_pres_t *pres);
+ua_pres_t* search_puadb(ua_pres_t *pres, ua_pres_t *result, db1_res_t **res );
+ua_pres_t* get_dialog_puadb(ua_pres_t *pres, ua_pres_t *result, db1_res_t **res);
+int is_dialog_puadb(ua_pres_t *pres);
+int get_record_id_puadb(ua_pres_t *pres, str **rec_id );
+int delete_temporary_dialog_puadb(ua_pres_t *pres );
+int delete_puadb(ua_pres_t *pres );
+int update_version_puadb(ua_pres_t *pres, int version );
+int update_contact_puadb(ua_pres_t *pres, str *contact);
+void update_puadb(ua_pres_t* pres, time_t desired_expires, 
+                       int expires, str* etag, str *contact);
+void insert_puadb(ua_pres_t* pres);
+
+
+#endif

+ 119 - 30
modules_k/pua/send_publish.c

@@ -47,6 +47,7 @@
 #include "send_publish.h"
 #include "pua_callback.h"
 #include "event_list.h"
+#include "pua_db.h"
 
 
 str* publ_build_hdr(int expires, pua_event_t* ev, str* content_type, str* etag,
@@ -150,12 +151,21 @@ void publ_cback_func(struct cell *t, int type, struct tmcb_params *ps)
 	struct hdr_field* hdr= NULL;
 	struct sip_msg* msg= NULL;
 	ua_pres_t* presentity= NULL;
+	ua_pres_t* db_presentity= NULL; 
 	ua_pres_t* hentity= NULL;
 	int found = 0;
 	int size= 0;
 	unsigned int lexpire= 0;
 	str etag;
 	unsigned int hash_code;
+	db1_res_t *res=NULL;
+	ua_pres_t dbpres;
+	str pres_uri={0,0}, watcher_uri={0,0}, extra_headers={0,0};
+
+	memset(&dbpres, 0, sizeof(dbpres));
+	dbpres.pres_uri = &pres_uri;
+	dbpres.watcher_uri = &watcher_uri;
+	dbpres.extra_headers = &extra_headers;
 
 	if(ps->param== NULL|| *ps->param== NULL)
 	{
@@ -180,19 +190,28 @@ void publ_cback_func(struct cell *t, int type, struct tmcb_params *ps)
 
 	if( ps->code>= 300 )
 	{
-		hash_code= core_hash(hentity->pres_uri, NULL,HASH_SIZE);
-		lock_get(&HashT->p_records[hash_code].lock);
-		presentity= search_htable( hentity, hash_code);
-		if(presentity)
+
+		if(dbmode==PUA_DB_ONLY)
 		{
-			LM_DBG("Record found in table and deleted\n");
-			delete_htable(presentity, hash_code);
+			delete_puadb(hentity);
 		}
 		else
 		{
-			LM_DBG("Record not found in table\n");
+			hash_code= core_hash(hentity->pres_uri, NULL,HASH_SIZE);
+			lock_get(&HashT->p_records[hash_code].lock);
+			presentity= search_htable( hentity, hash_code);
+			if(presentity)
+			{
+				LM_DBG("Record found in table and deleted\n");
+				delete_htable(presentity, hash_code);
+			}
+			else
+			{
+				LM_DBG("Record not found in table\n");
+			}
+
+			lock_release(&HashT->p_records[hash_code].lock);
 		}
-		lock_release(&HashT->p_records[hash_code].lock);
 
 		if(ps->code== 412 && hentity->body && hentity->flag!= MI_PUBLISH
 				&& hentity->flag!= MI_ASYN_PUBLISH)
@@ -227,7 +246,7 @@ void publ_cback_func(struct cell *t, int type, struct tmcb_params *ps)
 			}
 		}
 		goto done;
-	}
+	} /* code >= 300 */
 	
 	if( parse_headers(msg,HDR_EOH_F, 0)==-1 )
 	{
@@ -268,13 +287,36 @@ void publ_cback_func(struct cell *t, int type, struct tmcb_params *ps)
 	LM_DBG("completed with status %d [contact:%.*s]\n",
 			ps->code, hentity->pres_uri->len, hentity->pres_uri->s);
 
-	hash_code= core_hash(hentity->pres_uri, NULL, HASH_SIZE);
-	lock_get(&HashT->p_records[hash_code].lock);
-	
-	presentity= search_htable(hentity, hash_code);
-	if(presentity)
+	if (dbmode==PUA_DB_ONLY)
 	{
-			LM_DBG("update record\n");
+		db_presentity = search_puadb(hentity,&dbpres, &res);
+		/* this needs the columns used in update_db only */
+
+		if(db_presentity)
+		{
+			LM_DBG("update DB record\n");
+			if(lexpire == 0)
+			{
+				LM_DBG("expires= 0- delete from htable\n"); 
+				delete_puadb(hentity);
+				goto done;
+			}
+			
+			update_puadb( db_presentity, hentity->desired_expires, lexpire, &etag, NULL );
+			goto done;
+		}
+
+	}
+	else
+	{
+		hash_code= core_hash(hentity->pres_uri, NULL, HASH_SIZE);
+		lock_get(&HashT->p_records[hash_code].lock);
+		presentity= search_htable(hentity, hash_code);
+
+		if(presentity)
+		{
+			LM_DBG("update hash record\n");
+
 			if(lexpire == 0)
 			{
 				LM_DBG("expires= 0- delete from htable\n"); 
@@ -287,8 +329,10 @@ void publ_cback_func(struct cell *t, int type, struct tmcb_params *ps)
 					lexpire, &etag, hash_code, NULL);
 			lock_release(&HashT->p_records[hash_code].lock);
 			goto done;
+		}
+
+		lock_release(&HashT->p_records[hash_code].lock);
 	}
-	lock_release(&HashT->p_records[hash_code].lock);
 
 	if(lexpire== 0)
 	{
@@ -357,8 +401,15 @@ void publ_cback_func(struct cell *t, int type, struct tmcb_params *ps)
 	memcpy(presentity->etag.s, etag.s, etag.len);
 	presentity->etag.len= etag.len;
 
-	insert_htable( presentity);
-	LM_DBG("***Inserted in hash table\n");		
+	if (dbmode==PUA_DB_ONLY)
+	{
+		insert_puadb(presentity);
+	}
+	else
+ 	{
+		insert_htable(presentity);
+	}
+	LM_DBG("***Inserted in hash table\n");
 
 done:
 	if(hentity->ua_flag == REQ_OTHER)
@@ -370,6 +421,7 @@ done:
 		shm_free(*ps->param);
 		*ps->param= NULL;
 	}
+	free_results_puadb(res);
 	return;
 
 error:
@@ -381,6 +433,7 @@ error:
 	if(presentity)
 		shm_free(presentity);
 
+	free_results_puadb(res);
 	return;
 }	
 
@@ -392,13 +445,16 @@ int send_publish( publ_info_t* publ )
 	str* body= NULL;
 	str* tuple_id= NULL;
 	ua_pres_t* cb_param= NULL, pres;
-	unsigned int hash_code;
+	unsigned int hash_code=0;
 	str etag= {0, 0};
 	int ver= 0;
 	int result;
 	int ret_code= 0;
 	pua_event_t* ev= NULL;
 	uac_req_t uac_r;
+	db1_res_t *res=NULL;
+	ua_pres_t dbpres; 
+	str pres_uri={0,0}, watcher_uri={0,0}, extra_headers={0,0};
 
 	LM_DBG("pres_uri=%.*s\n", publ->pres_uri->len, publ->pres_uri->s );
 	
@@ -419,15 +475,29 @@ int send_publish( publ_info_t* publ )
 	if(publ->etag)
 		pres.etag= *publ->etag;
 
-	hash_code= core_hash(publ->pres_uri, NULL, HASH_SIZE);
+	if (dbmode==PUA_DB_ONLY)
+	{
+		/* do db specific stuff-pjp */
+		memset(&dbpres, 0, sizeof(dbpres));
+		dbpres.pres_uri = &pres_uri;
+		dbpres.watcher_uri = &watcher_uri;
+		dbpres.extra_headers = &extra_headers;
+		presentity = search_puadb(&pres, &dbpres, &res);
+	}
+	else
+	{
+		hash_code= core_hash(publ->pres_uri, NULL, HASH_SIZE);
 
-	lock_get(&HashT->p_records[hash_code].lock);
+		lock_get(&HashT->p_records[hash_code].lock);
 	
-	presentity= search_htable(&pres, hash_code);
+		presentity= search_htable(&pres, hash_code);
+	}
 
 	if(publ->etag && presentity== NULL)
 	{
-		lock_release(&HashT->p_records[hash_code].lock);
+		if (dbmode!=PUA_DB_ONLY) 
+			lock_release(&HashT->p_records[hash_code].lock);
+		free_results_puadb(res);
 		return 418;
 	}
 
@@ -440,7 +510,8 @@ int send_publish( publ_info_t* publ )
 	if(presentity== NULL)
 	{
 insert:	
-		lock_release(&HashT->p_records[hash_code].lock);
+		if (dbmode!=PUA_DB_ONLY) 
+			lock_release(&HashT->p_records[hash_code].lock);
 		LM_DBG("insert type\n"); 
 		
 		if(publ->flag & UPDATE_TYPE )
@@ -452,11 +523,13 @@ insert:
 		{
 			LM_DBG("request for a publish with expires 0 and"
 					" no record found\n");
+			free_results_puadb(res);
 			return 0;
 		}
 		if(publ->body== NULL)
 		{
 			LM_ERR("New PUBLISH and no body found- invalid request\n");
+			free_results_puadb(res);
 			return ERR_PUBLISH_NO_BODY;
 		}
 	}
@@ -468,7 +541,9 @@ insert:
 		if(etag.s== NULL)
 		{
 			LM_ERR("while allocating memory\n");
-			lock_release(&HashT->p_records[hash_code].lock);
+			if (dbmode!=PUA_DB_ONLY) 
+				lock_release(&HashT->p_records[hash_code].lock);
+			free_results_puadb(res);
 			return -1;
 		}
 		memcpy(etag.s, presentity->etag.s, presentity->etag.len);
@@ -481,14 +556,16 @@ insert:
 			if(tuple_id== NULL)
 			{
 				LM_ERR("No more memory\n");
-				lock_release(&HashT->p_records[hash_code].lock);
+				if (dbmode!=PUA_DB_ONLY) 
+					lock_release(&HashT->p_records[hash_code].lock);
 				goto error;
 			}	
 			tuple_id->s= (char*)pkg_malloc(presentity->tuple_id.len* sizeof(char));
 			if(tuple_id->s== NULL)
 			{
 				LM_ERR("No more memory\n");
-				lock_release(&HashT->p_records[hash_code].lock);
+				if (dbmode!=PUA_DB_ONLY) 
+					lock_release(&HashT->p_records[hash_code].lock);
 				goto error;
 			}	
 			memcpy(tuple_id->s, presentity->tuple_id.s, presentity->tuple_id.len);
@@ -498,12 +575,22 @@ insert:
 		if(publ->expires== 0)
 		{
 			LM_DBG("expires= 0- delete from hash table\n");
-			lock_release(&HashT->p_records[hash_code].lock);
+			if (dbmode!=PUA_DB_ONLY) 
+				lock_release(&HashT->p_records[hash_code].lock);
 			goto send_publish;
 		}
-		presentity->version++;
+
+		presentity->version++; 
 		ver= presentity->version;
-		lock_release(&HashT->p_records[hash_code].lock);
+
+		if (dbmode==PUA_DB_ONLY)
+		{ 
+			update_version_puadb(&pres,ver);
+		}
+		else
+		{
+			lock_release(&HashT->p_records[hash_code].lock);
+		}
 	}
 
     /* handle body */
@@ -583,6 +670,7 @@ send_publish:
 		pkg_free(tuple_id);
 	}
 
+	free_results_puadb(res);
 	return 0;
 
 error:
@@ -606,6 +694,7 @@ error:
 			pkg_free(tuple_id->s);
 		pkg_free(tuple_id);
 	}
+	free_results_puadb(res);
 	return -1;
 }
 

+ 141 - 40
modules_k/pua/send_subscribe.c

@@ -46,6 +46,7 @@
 #include "send_subscribe.h"
 #include "pua_callback.h"
 #include "event_list.h"
+#include "pua_db.h"
 
 
 void print_subs(subs_info_t* subs)
@@ -224,6 +225,14 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 	int rt;
 	str contact;
 	int initial_request = 0;
+	db1_res_t *res=NULL;
+ 	ua_pres_t dbpres;
+	str pres_uri={0,0}, watcher_uri={0,0}, extra_headers={0,0};
+
+	memset(&dbpres, 0, sizeof(dbpres));
+	dbpres.pres_uri = &pres_uri;
+	dbpres.watcher_uri = &watcher_uri;
+	dbpres.extra_headers = &extra_headers;
 
 	if( ps->param== NULL || *ps->param== NULL )
 	{
@@ -255,19 +264,34 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 			LM_DBG("initial Subscribe request failed\n");
 			goto done;
 		}
-		
-		lock_get(&HashT->p_records[hash_code].lock);
-		
-		presentity= get_dialog(hentity, hash_code);
+
+		if (dbmode==PUA_DB_ONLY)
+		{
+			presentity = get_dialog_puadb(hentity, &dbpres, &res);
+		}
+		else
+		{		
+			lock_get(&HashT->p_records[hash_code].lock);
+ 			presentity= get_dialog(hentity, hash_code);
+		}
+
 		if(presentity== NULL)
 		{
-			LM_ERR("no record found in hash table\n");
-			lock_release(&HashT->p_records[hash_code].lock);
+			LM_ERR("no record found\n");
+			if (dbmode!=PUA_DB_ONLY)
+				lock_release(&HashT->p_records[hash_code].lock);
 			goto done;
 		}
 
-		delete_htable(presentity, hash_code);
-		lock_release(&HashT->p_records[hash_code].lock);
+		if (dbmode==PUA_DB_ONLY)
+		{
+			delete_puadb(presentity);
+		}
+		else
+		{
+			delete_htable(presentity, hash_code);
+			lock_release(&HashT->p_records[hash_code].lock);
+		}
 		goto done;
 	}
 	
@@ -357,9 +381,15 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 		LM_DBG("lexpire= %d\n", lexpire);
 	}		
 
-	lock_get(&HashT->p_records[hash_code].lock);
-
-	presentity= get_dialog(hentity, hash_code);
+	if (dbmode==PUA_DB_ONLY)
+	{
+		presentity = get_dialog_puadb(hentity, &dbpres, &res);
+	}
+	else
+	{
+		lock_get(&HashT->p_records[hash_code].lock);
+		presentity= get_dialog(hentity, hash_code);
+	}
 
 	if(ps->code >= 300 )
 	{	/* if an error code and a stored dialog delete it and try to send 
@@ -369,8 +399,16 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 		{
 			subs_info_t subs;
 			hentity->event= presentity->event;
-			delete_htable(presentity, hash_code);
-			lock_release(&HashT->p_records[hash_code].lock);
+
+			if (dbmode==PUA_DB_ONLY)
+			{
+				delete_puadb(presentity);
+			}
+			else
+			{
+				delete_htable(presentity, hash_code);
+				lock_release(&HashT->p_records[hash_code].lock);
+			}
 
 			memset(&subs, 0, sizeof(subs_info_t));
 			subs.pres_uri= hentity->pres_uri; 
@@ -405,7 +443,8 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 		else 
 		{
 			LM_DBG("No dialog found\n");			
-			lock_release(&HashT->p_records[hash_code].lock);
+			if (dbmode!=PUA_DB_ONLY)
+				lock_release(&HashT->p_records[hash_code].lock);
 		}
 		goto done;
 	}
@@ -415,20 +454,23 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 	if(msg->contact== NULL || msg->contact->body.s== NULL)
 	{
 		LM_ERR("no contact header found");
-		lock_release(&HashT->p_records[hash_code].lock);
+		if (dbmode!=PUA_DB_ONLY)
+			lock_release(&HashT->p_records[hash_code].lock);
 		goto error;
 	}
 	if( parse_contact(msg->contact) <0 )
 	{
 		LM_ERR(" cannot parse contact header\n");
-		lock_release(&HashT->p_records[hash_code].lock);
+		if (dbmode!=PUA_DB_ONLY)
+			lock_release(&HashT->p_records[hash_code].lock);
 		goto error;
 	}
 
 	if(msg->contact->parsed == NULL)
 	{
 		LM_ERR("cannot parse contact header\n");
-		lock_release(&HashT->p_records[hash_code].lock);
+		if (dbmode!=PUA_DB_ONLY)
+			lock_release(&HashT->p_records[hash_code].lock);
 		goto error;
 	}
 	contact = ((contact_body_t* )msg->contact->parsed)->contacts->uri;
@@ -438,25 +480,40 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 		if(lexpire== 0 )
 		{
 			LM_DBG("lexpire= 0 Delete from hash table");
-			delete_htable(presentity, hash_code);
-			lock_release(&HashT->p_records[hash_code].lock);
+			if (dbmode==PUA_DB_ONLY)
+			{
+				delete_puadb(presentity);
+			}
+			else
+			{
+				delete_htable(presentity, hash_code);
+				lock_release(&HashT->p_records[hash_code].lock);
+			}
 			goto done;
 		}
 		LM_DBG("*** Update expires\n");
-		update_htable(presentity, hentity->desired_expires, lexpire, NULL,
+		if (dbmode==PUA_DB_ONLY)
+		{
+			update_puadb( presentity, hentity->desired_expires, lexpire, NULL, &contact );
+		}
+		else
+		{
+			update_htable(presentity, hentity->desired_expires, lexpire, NULL,
 				hash_code, &contact);
-		lock_release(&HashT->p_records[hash_code].lock);
+			lock_release(&HashT->p_records[hash_code].lock);
+		}
 		goto done;
 	}
 	if(initial_request == 0)
 	{
 		LM_ERR("Not initial request and no record found\n");
-		lock_release(&HashT->p_records[hash_code].lock);
+		if (dbmode!=PUA_DB_ONLY)
+			lock_release(&HashT->p_records[hash_code].lock);
 		goto error;
 	}
 
-
-	lock_release(&HashT->p_records[hash_code].lock);
+	if (dbmode!=PUA_DB_ONLY)
+		lock_release(&HashT->p_records[hash_code].lock);
 
 	/* if a new dialog -> insert */
 	if(lexpire== 0)
@@ -597,7 +654,15 @@ void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps)
 	LM_DBG("record for subscribe from %.*s to %.*s inserted in datatbase\n",
 			presentity->watcher_uri->len, presentity->watcher_uri->s,
 			presentity->pres_uri->len, presentity->pres_uri->s);
-	insert_htable(presentity);
+
+	if (dbmode==PUA_DB_ONLY)
+	{
+		insert_puadb(presentity);
+	}
+	else
+	{
+		insert_htable(presentity);
+	}
 
 done:
 	if(hentity->ua_flag == REQ_OTHER)
@@ -606,11 +671,19 @@ done:
 		run_pua_callbacks( hentity, msg);
 	}
 error:	
-	lock_get(&HashT->p_records[hash_code].lock);
-	presentity = get_temporary_dialog(hentity, hash_code);
-	if (presentity!=NULL)
-		delete_htable(presentity, hash_code);
-	lock_release(&HashT->p_records[hash_code].lock);
+	if (dbmode == PUA_DB_ONLY)
+	{
+		if (presentity!=NULL)
+			delete_temporary_dialog_puadb(presentity);
+	}
+	else
+	{
+		lock_get(&HashT->p_records[hash_code].lock);
+		presentity = get_temporary_dialog(hentity, hash_code);
+		if (presentity!=NULL)
+			delete_htable(presentity, hash_code);
+		lock_release(&HashT->p_records[hash_code].lock);
+	}
 
 	if(hentity)
 	{	
@@ -619,6 +692,7 @@ error:
 	}
 
 	free_to_params(&TO);
+	free_results_puadb(res);
 	return;
 }
 
@@ -813,12 +887,20 @@ int send_subscribe(subs_info_t* subs)
 	str met= {"SUBSCRIBE", 9};
 	str* str_hdr= NULL;
 	int ret= 0;
-	unsigned int hash_code;
+	unsigned int hash_code=0;
 	ua_pres_t* hentity= NULL, pres;
 	int expires;
 	int flag;
 	int result;
 	uac_req_t uac_r;
+	db1_res_t *res=NULL;
+	ua_pres_t dbpres;
+	str pres_uri={0,0}, watcher_uri={0,0}, extra_headers={0,0};
+
+	memset(&dbpres, 0, sizeof(dbpres));
+	dbpres.pres_uri = &pres_uri;
+	dbpres.watcher_uri = &watcher_uri;
+	dbpres.extra_headers = &extra_headers; 
 
 	print_subs(subs);
 
@@ -839,9 +921,7 @@ int send_subscribe(subs_info_t* subs)
 		return -1;
 	}
 
-	hash_code=core_hash(subs->pres_uri, subs->watcher_uri, HASH_SIZE);
-
-	lock_get(&HashT->p_records[hash_code].lock);
+	/* generation of hash and getting lock moved from here to further down */
 
 	memset(&pres, 0, sizeof(ua_pres_t));
 	pres.pres_uri= subs->pres_uri;
@@ -852,7 +932,16 @@ int send_subscribe(subs_info_t* subs)
 	if(subs->remote_target)
 		pres.remote_contact= *subs->remote_target;
 
-	presentity= search_htable(&pres, hash_code);
+	if (dbmode==PUA_DB_ONLY)
+	{
+		presentity = search_puadb(&pres, &dbpres, &res);
+	}
+	else
+	{
+		hash_code=core_hash(subs->pres_uri, subs->watcher_uri, HASH_SIZE);
+		lock_get(&HashT->p_records[hash_code].lock);
+		presentity= search_htable(&pres, hash_code);
+	}
 
 	/* if flag == INSERT_TYPE insert no matter what the search result is */
 	if(subs->flag & INSERT_TYPE)
@@ -865,7 +954,8 @@ int send_subscribe(subs_info_t* subs)
 	{
 		int size;
 insert:
-		lock_release(&HashT->p_records[hash_code].lock); 
+		if (dbmode!=PUA_DB_ONLY)
+			lock_release(&HashT->p_records[hash_code].lock); 
 		if(subs->flag & UPDATE_TYPE)
 		{
 			/*
@@ -965,7 +1055,14 @@ insert:
 		/* Set the temporary record expiry for 2 * 64T1 seconds from now */
 		presentity->expires= (int)time(NULL) + 64;
 
-		insert_htable(presentity);
+		if (dbmode==PUA_DB_ONLY)
+		{
+			insert_puadb(presentity);
+		}
+		else
+		{
+			insert_htable(presentity);
+		}
 
 		uac_r.dialog->rem_target.s = 0;
 		uac_r.dialog->dst_uri.s = 0;
@@ -1024,7 +1121,8 @@ insert:
 		{
 			LM_ERR("while building tm dlg_t structure");
 			ret= -1;
-			lock_release(&HashT->p_records[hash_code].lock);
+			if (dbmode!=PUA_DB_ONLY)
+				lock_release(&HashT->p_records[hash_code].lock);
 			goto done;
 		}
 				
@@ -1032,12 +1130,14 @@ insert:
 		if(hentity== NULL)
 		{
 			LM_ERR("while building callback param\n");
-			lock_release(&HashT->p_records[hash_code].lock);
+			if (dbmode!=PUA_DB_ONLY)
+				lock_release(&HashT->p_records[hash_code].lock);
 			ret= -1;
 			pkg_free(td);
 			goto done;
 		}
-		lock_release(&HashT->p_records[hash_code].lock);
+		if (dbmode!=PUA_DB_ONLY)
+			lock_release(&HashT->p_records[hash_code].lock);
 
 	//	hentity->flag= flag;
 		LM_DBG("event parameter: %d\n", hentity->event);	
@@ -1061,5 +1161,6 @@ insert:
 
 done:
 	pkg_free(str_hdr);
+	free_results_puadb(res);
 	return ret;
 }