Browse Source

New module p_usrloc

Description:

It is based on a usrloc module, but provides partitioned user location.
This provides 3 benefits:
1. Redundancy - if a location db is down, there is a spare running
2. Load Balancing - the module balances the entries by hashing the username(or username@domain)
3. Failover- in case of error and recovery, it ensures that no stale contacts are returned

More info in the READMEs(soon to come)
Marius Zbihlei 14 years ago
parent
commit
71a26065c3
54 changed files with 9200 additions and 0 deletions
  1. 161 0
      modules_k/p_usrloc/dlist.c
  2. 88 0
      modules_k/p_usrloc/dlist.h
  3. 206 0
      modules_k/p_usrloc/hslot.c
  4. 97 0
      modules_k/p_usrloc/hslot.h
  5. 20 0
      modules_k/p_usrloc/location.sql
  6. 17 0
      modules_k/p_usrloc/p_usrloc.sql
  7. 828 0
      modules_k/p_usrloc/ucontact.c
  8. 219 0
      modules_k/p_usrloc/ucontact.h
  9. 717 0
      modules_k/p_usrloc/udomain.c
  10. 194 0
      modules_k/p_usrloc/udomain.h
  11. 121 0
      modules_k/p_usrloc/ul_callback.c
  12. 95 0
      modules_k/p_usrloc/ul_callback.h
  13. 196 0
      modules_k/p_usrloc/ul_check.c
  14. 61 0
      modules_k/p_usrloc/ul_check.h
  15. 440 0
      modules_k/p_usrloc/ul_db.c
  16. 100 0
      modules_k/p_usrloc/ul_db.h
  17. 75 0
      modules_k/p_usrloc/ul_db_api.c
  18. 78 0
      modules_k/p_usrloc/ul_db_api.h
  19. 29 0
      modules_k/p_usrloc/ul_db_del.c
  20. 30 0
      modules_k/p_usrloc/ul_db_del.h
  21. 351 0
      modules_k/p_usrloc/ul_db_failover.c
  22. 41 0
      modules_k/p_usrloc/ul_db_failover.h
  23. 239 0
      modules_k/p_usrloc/ul_db_failover_func.c
  24. 40 0
      modules_k/p_usrloc/ul_db_failover_func.h
  25. 147 0
      modules_k/p_usrloc/ul_db_form_query.c
  26. 48 0
      modules_k/p_usrloc/ul_db_form_query.h
  27. 639 0
      modules_k/p_usrloc/ul_db_handle.c
  28. 86 0
      modules_k/p_usrloc/ul_db_handle.h
  29. 28 0
      modules_k/p_usrloc/ul_db_ins.c
  30. 30 0
      modules_k/p_usrloc/ul_db_ins.h
  31. 28 0
      modules_k/p_usrloc/ul_db_ins_upd.c
  32. 30 0
      modules_k/p_usrloc/ul_db_ins_upd.h
  33. 475 0
      modules_k/p_usrloc/ul_db_layer.c
  34. 59 0
      modules_k/p_usrloc/ul_db_layer.h
  35. 112 0
      modules_k/p_usrloc/ul_db_query.c
  36. 31 0
      modules_k/p_usrloc/ul_db_query.h
  37. 28 0
      modules_k/p_usrloc/ul_db_repl.c
  38. 30 0
      modules_k/p_usrloc/ul_db_repl.h
  39. 223 0
      modules_k/p_usrloc/ul_db_tran.c
  40. 36 0
      modules_k/p_usrloc/ul_db_tran.h
  41. 29 0
      modules_k/p_usrloc/ul_db_upd.c
  42. 31 0
      modules_k/p_usrloc/ul_db_upd.h
  43. 213 0
      modules_k/p_usrloc/ul_db_watch.c
  44. 38 0
      modules_k/p_usrloc/ul_db_watch.h
  45. 565 0
      modules_k/p_usrloc/ul_mi.c
  46. 101 0
      modules_k/p_usrloc/ul_mi.h
  47. 541 0
      modules_k/p_usrloc/ul_mod.c
  48. 150 0
      modules_k/p_usrloc/ul_mod.h
  49. 629 0
      modules_k/p_usrloc/urecord.c
  50. 192 0
      modules_k/p_usrloc/urecord.h
  51. 82 0
      modules_k/p_usrloc/usrloc.c
  52. 69 0
      modules_k/p_usrloc/usrloc.h
  53. 42 0
      modules_k/p_usrloc/utime.c
  54. 45 0
      modules_k/p_usrloc/utime.h

+ 161 - 0
modules_k/p_usrloc/dlist.c

@@ -0,0 +1,161 @@
+/*
+ * $Id: dlist.c 5160 2008-11-03 17:51:22Z henningw $
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ========
+ * 2006-11-28 added get_number_of_users() (Jeffrey Magder - SOMA Networks)
+ * 2007-09-12 added partitioning support for fetching all ul contacts
+ *            (bogdan)
+ */
+
+/*! \file
+ *  \brief USRLOC - List of registered domains
+ *  \ingroup usrloc
+ *
+ * - Module: \ref usrloc
+ */
+
+
+#include "dlist.h"
+#include <stdlib.h>	       /* abort */
+#include <string.h>            /* strlen, memcmp */
+#include <stdio.h>             /* printf */
+#include "../../ut.h"
+#include "../../lib/srdb1/db.h"
+#include "../../mem/shm_mem.h"
+#include "../../dprint.h"
+#include "../../ip_addr.h"
+#include "../../socket_info.h"
+#include "udomain.h"           /* new_udomain, free_udomain */
+#include "utime.h"
+#include "ul_mod.h"
+
+#include "ul_db_layer.h"
+
+static struct domain_list_item *domain_list;
+
+
+
+static inline struct domain_list_item * find_dlist (str *name) {
+	struct domain_list_item *item;
+
+	for (item = domain_list; item != NULL; item = item->next) {
+		if (item->name.len == name->len
+		        && memcmp (item->name.s, name->s, name->len) == 0) {
+			return item;
+		}
+	}
+	return NULL;
+}
+
+
+
+
+static inline struct domain_list_item * add_to_dlist (str *name, int type) {
+	struct domain_list_item *item;
+
+	item = (struct domain_list_item *)
+	       pkg_malloc (sizeof (struct domain_list_item));
+	if (item == NULL) {
+		LM_ERR("Out of shared memory.\n");
+		return NULL;
+	}
+	item->name.s = (char *) pkg_malloc (name->len + 1);
+	if (item->name.s == NULL) {
+		LM_ERR("Out of shared memory.\n");
+		return NULL;
+	}
+	memcpy (item->name.s, name->s, name->len);
+	item->name.s[name->len] = '\0';
+	item->name.len = name->len;
+
+	memset (&item->domain, 0, sizeof (struct udomain));
+	item->domain.name = &item->name;
+	item->domain.dbt = type;
+	/* Everything else is not useful for now.  */
+
+	item->next = domain_list;
+	domain_list = item;
+
+	return item;
+}
+
+
+/*!
+ * \brief Registers a new domain with usrloc
+ *
+ * Registers a new domain with usrloc. If the domain exists,
+ * a pointer to existing structure will be returned, otherwise
+ * a new domain will be created
+ * \param _n domain name
+ * \param _d new created domain
+ * \return 0 on success, -1 on failure
+ */
+int register_udomain(const char *name, udomain_t **domain) {
+	struct domain_list_item *item;
+	str name_str;
+	ul_domain_db_t * d;
+
+	name_str.s = (char *) name;
+	name_str.len = strlen (name);
+	item = find_dlist (&name_str);
+	if (item == NULL) {
+		if((d = ul_find_domain(name)) == NULL){
+			LM_ERR("domain %s not found.\n", name);
+			return -1;
+		}
+		item = add_to_dlist (&name_str, d->dbt);
+	}
+	if (item == NULL) {
+		return -1;
+	}
+	*domain = &item->domain;
+	LM_DBG("found domain %.*s, type: %s\n", (*domain)->name->len, (*domain)->name->s, (((*domain)->dbt) == DB_TYPE_CLUSTER ? "cluster" : "single"));
+	return 0;
+}
+
+
+
+
+/*!
+ * \brief Loops through all domains summing up the number of users
+ * \return the number of users, could be zero
+ */
+unsigned long get_number_of_users(void)
+{
+	int numberOfUsers = 0;
+	LM_INFO("not available with sp-ul_db interface");
+	return numberOfUsers;
+}
+
+
+/*!
+ * \brief Run timer handler of all domains
+ * \return 0 if all timer return 0, != 0 otherwise
+ */
+int synchronize_all_udomains(void)
+{
+	int res = 0;
+	LM_INFO("not available with sp-ul_db interface");
+	return res;
+}
+
+

+ 88 - 0
modules_k/p_usrloc/dlist.h

@@ -0,0 +1,88 @@
+/*
+ * $Id: dlist.h 5160 2008-11-03 17:51:22Z henningw $
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ========
+ * 2006-11-28 Added get_number_of_users() (Jeffrey Magder - SOMA Networks)
+ * 2007-09-12 added partitioning support for fetching all ul contacts
+ *            (bogdan)
+ */
+
+/*! \file
+ *  \brief USRLOC - List of registered domains
+ *  \ingroup usrloc
+ */
+
+
+#ifndef DLIST_H
+#define DLIST_H
+
+#include <stdio.h>
+#include "udomain.h"
+#include "../../str.h"
+
+
+/*!
+ * List of all domains registered with usrloc
+ */
+struct domain_list_item {
+	str name;
+	udomain_t domain;
+	struct domain_list_item*next;
+};
+
+/*!
+ * \brief Registers a new domain with usrloc
+ *
+ * Registers a new domain with usrloc. If the domain exists,
+ * a pointer to existing structure will be returned, otherwise
+ * a new domain will be created
+ * \param _n domain name
+ * \param _d new created domain
+ * \return 0 on success, -1 on failure
+ */
+typedef int (*register_udomain_t)(const char* _n, udomain_t** _d);
+int register_udomain(const char* _n, udomain_t** _d);
+
+
+/*!
+ * \brief Free all allocated memory for domains
+ */
+void free_all_udomains(void);
+
+typedef int  (*get_all_ucontacts_t) (void* buf, int len, unsigned int flags,
+              unsigned int part_idx, unsigned int part_max);
+
+/*!
+ * \brief Run timer handler of all domains
+ * \return 0 if all timer return 0, != 0 otherwise
+ */
+int synchronize_all_udomains(void);
+
+/*!
+ * \brief Loops through all domains summing up the number of users
+ * \return the number of users, could be zero
+ */
+unsigned long get_number_of_users(void);
+
+
+
+#endif

+ 206 - 0
modules_k/p_usrloc/hslot.c

@@ -0,0 +1,206 @@
+/*
+ * $Id: hslot.c 5241 2008-11-21 12:52:25Z henningw $ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*! \file
+ *  \brief USRLOC - Hash table collision slot related functions
+ *  \ingroup usrloc
+ *
+ * - Module: \ref usrloc
+ */
+
+
+
+#include "hslot.h"
+
+/*! number of locks */
+int ul_locks_no=4;
+/*! global list of locks */
+gen_lock_set_t* ul_locks=0;
+
+
+/*!
+ * \brief Initialize locks for the hash table
+ * \return 0 on success, -1 on failure
+ */
+int ul_init_locks(void)
+{
+	int i;
+	i = ul_locks_no;
+	do {
+		if ((( ul_locks=lock_set_alloc(i))!=0)&&
+				(lock_set_init(ul_locks)!=0))
+		{
+			ul_locks_no = i;
+			LM_INFO("locks array size %d\n", ul_locks_no);
+			return 0;
+
+		}
+		if (ul_locks){
+			lock_set_dealloc(ul_locks);
+			ul_locks=0;
+		}
+		i--;
+		if(i==0)
+		{
+			LM_ERR("failed to allocate locks\n");
+			return -1;
+		}
+	} while (1);
+}
+
+
+/*!
+ * \brief Unlock all locks on the list
+ */
+void ul_unlock_locks(void)
+{
+	unsigned int i;
+
+	if (ul_locks==0)
+		return;
+
+	for (i=0;i<ul_locks_no;i++) {
+#ifdef GEN_LOCK_T_PREFERED
+		lock_release(&ul_locks->locks[i]);
+#else
+		ul_release_idx(i);
+#endif
+	};
+}
+
+
+/*!
+ * \brief Destroy all locks on the list
+ */
+void ul_destroy_locks(void)
+{
+	if (ul_locks !=0){
+		lock_set_destroy(ul_locks);
+		lock_set_dealloc(ul_locks);
+	};
+}
+
+#ifndef GEN_LOCK_T_PREFERED
+/*!
+ * \brief Lock a lock with a certain index
+ * \param idx lock index
+ */
+void ul_lock_idx(int idx)
+{
+	lock_set_get(ul_locks, idx);
+}
+
+
+/*!
+ * \brief Release a lock with a certain index
+ * \param idx lock index
+ */
+void ul_release_idx(int idx)
+{
+	lock_set_release(ul_locks, idx);
+}
+#endif
+
+/*!
+ * \brief Initialize cache slot structure
+ * \param _d domain for the hash slot
+ * \param _s hash slot
+ * \param n used to get the slot number (modulo number or locks)
+ */
+void init_slot(struct udomain* _d, hslot_t* _s, int n)
+{
+	_s->n = 0;
+	_s->first = 0;
+	_s->last = 0;
+	_s->d = _d;
+
+#ifdef GEN_LOCK_T_PREFERED
+	_s->lock = &ul_locks->locks[n%ul_locks_no];
+#else
+	_s->lockidx = n%ul_locks_no;
+#endif
+}
+
+
+/*!
+ * \brief Deinitialize given slot structure
+ * \param _s hash slot
+ */
+void deinit_slot(hslot_t* _s)
+{
+	struct urecord* ptr;
+	
+	     /* Remove all elements */
+	while(_s->first) {
+		ptr = _s->first;
+		_s->first = _s->first->next;
+		free_urecord(ptr);
+	}
+	
+	_s->n = 0;
+	_s->last = 0;
+    _s->d = 0;
+}
+
+
+/*!
+ * \brief Add an element to an slot's linked list
+ * \param _s hash slot
+ * \param _r added record
+ */
+void slot_add(hslot_t* _s, struct urecord* _r)
+{
+	if (_s->n == 0) {
+		_s->first = _s->last = _r;
+	} else {
+		_r->prev = _s->last;
+		_s->last->next = _r;
+		_s->last = _r;
+	}
+	_s->n++;
+	_r->slot = _s;
+}
+
+
+/*!
+ * \brief Remove an element from slot linked list
+ * \param _s hash slot
+ * \param _r removed record
+ */
+void slot_rem(hslot_t* _s, struct urecord* _r)
+{
+	if (_r->prev) {
+		_r->prev->next = _r->next;
+	} else {
+		_s->first = _r->next;
+	}
+
+	if (_r->next) {
+		_r->next->prev = _r->prev;
+	} else {
+		_s->last = _r->prev;
+	}
+
+	_r->prev = _r->next = 0;
+	_r->slot = 0;
+	_s->n--;
+}

+ 97 - 0
modules_k/p_usrloc/hslot.h

@@ -0,0 +1,97 @@
+/*
+ * $Id: hslot.h 5241 2008-11-21 12:52:25Z henningw $ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*! \file
+ *  \brief USRLOC - Hash table collision slot related functions
+ *  \ingroup usrloc
+ */
+
+
+
+#ifndef HSLOT_H
+#define HSLOT_H
+
+#include "../../locking.h"
+
+#include "udomain.h"
+#include "urecord.h"
+
+
+struct udomain;
+struct urecord;
+
+
+typedef struct hslot {
+	int n;                  /*!< Number of elements in the collision slot */
+	struct urecord* first;  /*!< First element in the list */
+	struct urecord* last;   /*!< Last element in the list */
+	struct udomain* d;      /*!< Domain we belong to */
+#ifdef GEN_LOCK_T_PREFERED
+	gen_lock_t *lock;       /*!< Lock for hash entry - fastlock */
+#else
+	int lockidx;            /*!< Lock index for hash entry - the rest*/
+#endif
+} hslot_t;
+
+/*! \brief
+ * Initialize slot structure
+ */
+void init_slot(struct udomain* _d, hslot_t* _s, int n);
+
+
+/*! \brief
+ * Deinitialize given slot structure
+ */
+void deinit_slot(hslot_t* _s);
+
+
+/*! \brief
+ * Add an element to slot linked list
+ */
+void slot_add(hslot_t* _s, struct urecord* _r);
+
+
+/*! \brief
+ * Remove an element from slot linked list
+ */
+void slot_rem(hslot_t* _s, struct urecord* _r);
+
+
+/*!
+ * \brief Initialize locks for the hash table
+ * \return 0 on success, -1 on failure
+ */
+int ul_init_locks(void);
+
+
+/*!
+ * \brief Destroy all locks on the list
+ */
+void ul_unlock_locks(void);
+void ul_destroy_locks(void);
+
+#ifndef GEN_LOCK_T_PREFERED
+void ul_lock_idx(int idx);
+void ul_release_idx(int idx);
+#endif
+
+#endif /* HSLOT_H */

+ 20 - 0
modules_k/p_usrloc/location.sql

@@ -0,0 +1,20 @@
+drop table location;
+CREATE TABLE location (
+username VARCHAR(64) NOT NULL,
+aor VARCHAR(255) NOT NULL,
+contact VARCHAR(255) NOT NULL,
+server_id INT NOT NULL DEFAULT '0',
+received VARCHAR(255),
+expires DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00',
+q FLOAT NOT NULL DEFAULT '1.0',
+callid VARCHAR(255),
+cseq INT UNSIGNED,
+flags INT UNSIGNED NOT NULL DEFAULT '0',
+cflags INT UNSIGNED NOT NULL DEFAULT '0',
+user_agent VARCHAR(64),
+instance VARCHAR(255),
+UNIQUE KEY location_key (username, contact),
+KEY location_contact (contact),
+KEY location_expires (expires)
+);
+

+ 17 - 0
modules_k/p_usrloc/p_usrloc.sql

@@ -0,0 +1,17 @@
+--
+-- Table structure for table `locdb`
+--
+
+DROP TABLE IF EXISTS `locdb`;
+CREATE TABLE `locdb` (
+  `id` int(11) NOT NULL default '0',
+  `no` int(11) NOT NULL default '0',
+  `url` varchar(255) NOT NULL default '',
+  `status` tinyint(4) NOT NULL default '1',
+  `errors` int(11) NOT NULL default '0',
+  `failover` datetime NOT NULL default '1900-01-01 00:00:01',
+  `spare` tinyint(4) NOT NULL default '0',
+  `rg` int(11) NOT NULL default '0',
+  PRIMARY KEY  (`id`,`no`),
+  KEY `no` (`no`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;

+ 828 - 0
modules_k/p_usrloc/ucontact.c

@@ -0,0 +1,828 @@
+/*
+ * $Id: ucontact.c 5272 2008-11-27 12:32:26Z henningw $ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ---------
+ * 2003-03-12 added replication mark and three zombie states (nils)
+ * 2004-03-17 generic callbacks added (bogdan)
+ * 2004-06-07 updated to the new DB api (andrei)
+ */
+
+/*! \file
+ *  \brief USRLOC - Usrloc contact handling functions
+ *  \ingroup usrloc
+ *
+ * - Module: \ref usrloc
+ */
+
+#include "ucontact.h"
+#include <string.h>             /* memcpy */
+#include "../../mem/shm_mem.h"
+#include "../../ut.h"
+#include "../../ip_addr.h"
+#include "../../socket_info.h"
+#include "../../dprint.h"
+#include "../../lib/srdb1/db.h"
+#include "ul_mod.h"
+#include "ul_callback.h"
+#include "urecord.h"
+#include "ucontact.h"
+#include "ul_db_layer.h"
+
+
+/*!
+ * \brief Create a new contact structure
+ * \param _dom domain
+ * \param _aor address of record
+ * \param _contact contact string
+ * \param _ci contact informations
+ * \return new created contact on success, 0 on failure
+ */
+ucontact_t* new_ucontact(str* _dom, str* _aor, str* _contact, ucontact_info_t* _ci)
+{
+	ucontact_t *c;
+
+	c = (ucontact_t*)shm_malloc(sizeof(ucontact_t));
+	if (!c) {
+		LM_ERR("no more shm memory\n");
+		return 0;
+	}
+	memset(c, 0, sizeof(ucontact_t));
+
+	if (shm_str_dup( &c->c, _contact) < 0) goto error;
+	if (shm_str_dup( &c->callid, _ci->callid) < 0) goto error;
+	if (shm_str_dup( &c->user_agent, _ci->user_agent) < 0) goto error;
+
+	if (_ci->received.s && _ci->received.len) {
+		if (shm_str_dup( &c->received, &_ci->received) < 0) goto error;
+	}
+	if (_ci->path && _ci->path->len) {
+		if (shm_str_dup( &c->path, _ci->path) < 0) goto error;
+	}
+
+	c->domain = _dom;
+	c->aor = _aor;
+	c->expires = _ci->expires;
+	c->q = _ci->q;
+	c->sock = _ci->sock;
+	c->cseq = _ci->cseq;
+	c->state = CS_NEW;
+	c->flags = _ci->flags;
+	c->cflags = _ci->cflags;
+	c->methods = _ci->methods;
+	c->last_modified = _ci->last_modified;
+
+	return c;
+error:
+	LM_ERR("no more shm memory\n");
+	if (c->path.s) shm_free(c->path.s);
+	if (c->received.s) shm_free(c->received.s);
+	if (c->user_agent.s) shm_free(c->user_agent.s);
+	if (c->callid.s) shm_free(c->callid.s);
+	if (c->c.s) shm_free(c->c.s);
+	shm_free(c);
+	return 0;
+}
+
+
+
+/*!
+ * \brief Free all memory associated with given contact structure
+ * \param _c freed contact
+ */
+void free_ucontact(ucontact_t* _c)
+{
+	if (!_c) return;
+	if (_c->path.s) shm_free(_c->path.s);
+	if (_c->received.s) shm_free(_c->received.s);
+	if (_c->user_agent.s) shm_free(_c->user_agent.s);
+	if (_c->callid.s) shm_free(_c->callid.s);
+	if (_c->c.s) shm_free(_c->c.s);
+	shm_free( _c );
+}
+
+
+/*!
+ * \brief Print contact, for debugging purposes only
+ * \param _f output file
+ * \param _c printed contact
+ */
+void print_ucontact(FILE* _f, ucontact_t* _c)
+{
+	time_t t = time(0);
+	char* st;
+
+	switch(_c->state) {
+	case CS_NEW:   st = "CS_NEW";     break;
+	case CS_SYNC:  st = "CS_SYNC";    break;
+	case CS_DIRTY: st = "CS_DIRTY";   break;
+	default:       st = "CS_UNKNOWN"; break;
+	}
+
+	fprintf(_f, "~~~Contact(%p)~~~\n", _c);
+	fprintf(_f, "domain    : '%.*s'\n", _c->domain->len, ZSW(_c->domain->s));
+	fprintf(_f, "aor       : '%.*s'\n", _c->aor->len, ZSW(_c->aor->s));
+	fprintf(_f, "Contact   : '%.*s'\n", _c->c.len, ZSW(_c->c.s));
+	fprintf(_f, "Expires   : ");
+	if (_c->expires == 0) {
+		fprintf(_f, "Permanent\n");
+	} else if (_c->expires == UL_EXPIRED_TIME) {
+		fprintf(_f, "Deleted\n");
+	} else if (t > _c->expires) {
+		fprintf(_f, "Expired\n");
+	} else {
+		fprintf(_f, "%u\n", (unsigned int)(_c->expires - t));
+	}
+	fprintf(_f, "q         : %s\n", q2str(_c->q, 0));
+	fprintf(_f, "Call-ID   : '%.*s'\n", _c->callid.len, ZSW(_c->callid.s));
+	fprintf(_f, "CSeq      : %d\n", _c->cseq);
+	fprintf(_f, "User-Agent: '%.*s'\n",
+		_c->user_agent.len, ZSW(_c->user_agent.s));
+	fprintf(_f, "received  : '%.*s'\n",
+		_c->received.len, ZSW(_c->received.s));
+	fprintf(_f, "Path      : '%.*s'\n",
+		_c->path.len, ZSW(_c->path.s));
+	fprintf(_f, "State     : %s\n", st);
+	fprintf(_f, "Flags     : %u\n", _c->flags);
+	if (_c->sock) {
+		fprintf(_f, "Sock      : %.*s (%p)\n",
+				_c->sock->sock_str.len,_c->sock->sock_str.s,_c->sock);
+	} else {
+		fprintf(_f, "Sock      : none (null)\n");
+	}
+	fprintf(_f, "Methods   : %u\n", _c->methods);
+	fprintf(_f, "next      : %p\n", _c->next);
+	fprintf(_f, "prev      : %p\n", _c->prev);
+	fprintf(_f, "~~~/Contact~~~~\n");
+}
+
+
+/*!
+ * \brief Update existing contact in memory with new values
+ * \param _c contact
+ * \param _ci contact informations
+ * \return 0 on success, -1 on failure
+ */
+int mem_update_ucontact(ucontact_t* _c, ucontact_info_t* _ci)
+{
+#define update_str(_old,_new) \
+	do{\
+		if ((_old)->len < (_new)->len) { \
+			ptr = (char*)shm_malloc((_new)->len); \
+			if (ptr == 0) { \
+				LM_ERR("no more shm memory\n"); \
+				return -1; \
+			}\
+			memcpy(ptr, (_new)->s, (_new)->len);\
+			if ((_old)->s) shm_free((_old)->s);\
+			(_old)->s = ptr;\
+		} else {\
+			memcpy((_old)->s, (_new)->s, (_new)->len);\
+		}\
+		(_old)->len = (_new)->len;\
+	} while(0)
+
+	char* ptr;
+
+	/* No need to update Callid as it is constant 
+	 * per ucontact (set at insert time)  -bogdan */
+
+	update_str( &_c->user_agent, _ci->user_agent);
+
+	if (_ci->received.s && _ci->received.len) {
+		update_str( &_c->received, &_ci->received);
+	} else {
+		if (_c->received.s) shm_free(_c->received.s);
+		_c->received.s = 0;
+		_c->received.len = 0;
+	}
+	
+	if (_ci->path) {
+		update_str( &_c->path, _ci->path);
+	} else {
+		if (_c->path.s) shm_free(_c->path.s);
+		_c->path.s = 0;
+		_c->path.len = 0;
+	}
+
+	_c->sock = _ci->sock;
+	_c->expires = _ci->expires;
+	_c->q = _ci->q;
+	_c->cseq = _ci->cseq;
+	_c->methods = _ci->methods;
+	_c->last_modified = _ci->last_modified;
+	_c->flags = _ci->flags;
+	_c->cflags = _ci->cflags;
+
+	return 0;
+}
+
+
+/* ================ State related functions =============== */
+
+/*!
+ * \brief Update state of the contact if we are using write-back scheme
+ * \param _c updated contact
+ */
+void st_update_ucontact(ucontact_t* _c)
+{
+	switch(_c->state) {
+	case CS_NEW:
+			 /* Contact is new and is not in the database yet,
+			  * we remain in the same state here because the
+			  * contact must be inserted later in the timer
+			  */
+		break;
+
+	case CS_SYNC:
+			 /* For db mode 1 & 2 a modified contact needs to be 
+			  * updated also in the database, so transit into 
+			  * CS_DIRTY and let the timer to do the update 
+			  * again. For db mode 1 we try to update right
+			  * now and if fails, let the timer to do the job
+			  */
+		if (db_mode == WRITE_BACK || db_mode == WRITE_THROUGH) {
+			_c->state = CS_DIRTY;
+		}
+		break;
+
+	case CS_DIRTY:
+			 /* Modification of dirty contact results in
+			  * dirty contact again, don't change anything
+			  */
+		break;
+	}
+}
+
+
+/*!
+ * \brief Update state of the contact
+ * \param _c updated contact
+ * \return 1 if the contact should be deleted from memory immediately, 0 otherwise
+ */
+int st_delete_ucontact(ucontact_t* _c)
+{
+	switch(_c->state) {
+	case CS_NEW:
+		     /* Contact is new and isn't in the database
+		      * yet, we can delete it from the memory
+		      * safely.
+		      */
+		return 1;
+
+	case CS_SYNC:
+	case CS_DIRTY:
+		     /* Contact is in the database,
+		      * we cannot remove it from the memory 
+		      * directly, but we can set expires to zero
+		      * and the timer will take care of deleting 
+		      * the contact from the memory as well as 
+		      * from the database
+		      */
+		if (db_mode == WRITE_BACK) {
+			_c->expires = UL_EXPIRED_TIME;
+			return 0;
+		} else {
+			     /* WRITE_THROUGH or NO_DB -- we can
+			      * remove it from memory immediately and
+			      * the calling function would also remove
+			      * it from the database if needed
+			      */
+			return 1;
+		}
+	}
+
+	return 0; /* Makes gcc happy */
+}
+
+
+/*!
+ * \brief Called when the timer is about to delete an expired contact
+ * \param _c expired contact
+ * \return 1 if the contact should be removed from the database and 0 otherwise
+ */
+int st_expired_ucontact(ucontact_t* _c)
+{
+	     /* There is no need to change contact
+	      * state, because the contact will
+	      * be deleted anyway
+	      */
+
+	switch(_c->state) {
+	case CS_NEW:
+		     /* Contact is not in the database
+		      * yet, remove it from memory only
+		      */
+		return 0;
+
+	case CS_SYNC:
+	case CS_DIRTY:
+		     /* Remove from database here */
+		return 1;
+	}
+
+	return 0; /* Makes gcc happy */
+}
+
+
+/*!
+ * \brief Called when the timer is about flushing the contact, updates contact state
+ * \param _c flushed contact
+ * \return 1 if the contact should be inserted, 2 if update and 0 otherwise
+ */
+int st_flush_ucontact(ucontact_t* _c)
+{
+	switch(_c->state) {
+	case CS_NEW:
+		     /* Contact is new and is not in
+		      * the database yet so we have
+		      * to insert it
+		      */
+		_c->state = CS_SYNC;
+		return 1;
+
+	case CS_SYNC:
+		     /* Contact is synchronized, do
+		      * nothing
+		      */
+		return 0;
+
+	case CS_DIRTY:
+		     /* Contact has been modified and
+		      * is in the db already so we
+		      * have to update it
+		      */
+		_c->state = CS_SYNC;
+		return 2;
+	}
+
+	return 0; /* Makes gcc happy */
+}
+
+
+/* ============== Database related functions ================ */
+
+/*!
+ * \brief Insert contact into the database
+ * \param _c inserted contact
+ * \return 0 on success, -1 on failure
+ */
+int db_insert_ucontact(ucontact_t* _c)
+{
+	char* dom;
+	db_key_t keys[15];
+	db_val_t vals[15];
+	
+	if (_c->flags & FL_MEM) {
+		return 0;
+	}
+
+	struct udomain * _d;
+	if(register_udomain(_c->domain->s, &_d) < 0){
+		return -1;
+	}
+
+	keys[0] = &user_col;
+	keys[1] = &contact_col;
+	keys[2] = &expires_col;
+	keys[3] = &q_col;
+	keys[4] = &callid_col;
+	keys[5] = &cseq_col;
+	keys[6] = &flags_col;
+	keys[7] = &cflags_col;
+	keys[8] = &user_agent_col;
+	keys[9] = &received_col;
+	keys[10] = &path_col;
+	keys[11] = &sock_col;
+	keys[12] = &methods_col;
+	keys[13] = &last_mod_col;
+	keys[14] = &domain_col;
+
+	vals[0].type = DB1_STR;
+	vals[0].nul = 0;
+	vals[0].val.str_val.s = _c->aor->s;
+	vals[0].val.str_val.len = _c->aor->len;
+
+	vals[1].type = DB1_STR;
+	vals[1].nul = 0;
+	vals[1].val.str_val.s = _c->c.s; 
+	vals[1].val.str_val.len = _c->c.len;
+
+	vals[2].type = DB1_DATETIME;
+	vals[2].nul = 0;
+	vals[2].val.time_val = _c->expires;
+
+	vals[3].type = DB1_DOUBLE;
+	vals[3].nul = 0;
+	vals[3].val.double_val = q2double(_c->q);
+
+	vals[4].type = DB1_STR;
+	vals[4].nul = 0;
+	vals[4].val.str_val.s = _c->callid.s;
+	vals[4].val.str_val.len = _c->callid.len;
+
+	vals[5].type = DB1_INT;
+	vals[5].nul = 0;
+	vals[5].val.int_val = _c->cseq;
+
+	vals[6].type = DB1_INT;
+	vals[6].nul = 0;
+	vals[6].val.bitmap_val = _c->flags;
+
+	vals[7].type = DB1_INT;
+	vals[7].nul = 0;
+	vals[7].val.bitmap_val = _c->cflags;
+
+	vals[8].type = DB1_STR;
+	vals[8].nul = 0;
+	vals[8].val.str_val.s = _c->user_agent.s;
+	vals[8].val.str_val.len = _c->user_agent.len;
+
+	vals[9].type = DB1_STR;
+	if (_c->received.s == 0) {
+		vals[9].nul = 1;
+	} else {
+		vals[9].nul = 0;
+		vals[9].val.str_val.s = _c->received.s;
+		vals[9].val.str_val.len = _c->received.len;
+	}
+	
+	vals[10].type = DB1_STR;
+	if (_c->path.s == 0) {
+		vals[10].nul = 1;
+	} else {
+		vals[10].nul = 0;
+		vals[10].val.str_val.s = _c->path.s;
+		vals[10].val.str_val.len = _c->path.len;
+	}
+
+	vals[11].type = DB1_STR;
+	if (_c->sock) {
+		vals[11].val.str_val = _c->sock->sock_str;
+		vals[11].nul = 0;
+	} else {
+		vals[11].nul = 1;
+	}
+
+	vals[12].type = DB1_BITMAP;
+	if (_c->methods == 0xFFFFFFFF) {
+		vals[12].nul = 1;
+	} else {
+		vals[12].val.bitmap_val = _c->methods;
+		vals[12].nul = 0;
+	}
+
+	vals[13].type = DB1_DATETIME;
+	vals[13].nul = 0;
+	vals[13].val.time_val = _c->last_modified;
+
+	if (use_domain) {
+		vals[14].type = DB1_STR;
+		vals[14].nul = 0;
+
+		dom = memchr(_c->aor->s, '@', _c->aor->len);
+		if (dom==0) {
+			vals[0].val.str_val.len = 0;
+			vals[14].val.str_val = *_c->aor;
+		} else {
+			vals[0].val.str_val.len = dom - _c->aor->s;
+			vals[14].val.str_val.s = dom + 1;
+			vals[14].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1;
+		}
+	}
+	/* to prevent errors from the DB because of duplicated entries */
+	if (ul_db_layer_insert_update(_d, &vals[0].val.str_val, &vals[14].val.str_val, keys, vals, (use_domain) ? (15) : (14)) < 0) {	                
+		LM_ERR("inserting contact in db failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*!
+ * \brief Update contact in the database
+ * \param _c updated contact
+ * \return 0 on success, -1 on failure
+ */
+int db_update_ucontact(ucontact_t* _c)
+{
+	char* dom;
+	db_key_t keys1[4];
+	db_val_t vals1[4];
+
+	db_key_t keys2[11];
+	db_val_t vals2[11];
+
+	if (_c->flags & FL_MEM) {
+		return 0;
+	}
+	struct udomain * _d;
+	if(register_udomain(_c->domain->s, &_d) < 0){
+		return -1;
+	}
+
+	keys1[0] = &user_col;
+	keys1[1] = &contact_col;
+	keys1[2] = &callid_col;
+	keys1[3] = &domain_col;
+	keys2[0] = &expires_col;
+	keys2[1] = &q_col;
+	keys2[2] = &cseq_col;
+	keys2[3] = &flags_col;
+	keys2[4] = &cflags_col;
+	keys2[5] = &user_agent_col;
+	keys2[6] = &received_col;
+	keys2[7] = &path_col;
+	keys2[8] = &sock_col;
+	keys2[9] = &methods_col;
+	keys2[10] = &last_mod_col;
+
+	vals1[0].type = DB1_STR;
+	vals1[0].nul = 0;
+	vals1[0].val.str_val = *_c->aor;
+
+	vals1[1].type = DB1_STR;
+	vals1[1].nul = 0;
+	vals1[1].val.str_val = _c->c;
+
+	vals1[2].type = DB1_STR;
+	vals1[2].nul = 0;
+	vals1[2].val.str_val = _c->callid;
+
+	vals2[0].type = DB1_DATETIME;
+	vals2[0].nul = 0;
+	vals2[0].val.time_val = _c->expires;
+
+	vals2[1].type = DB1_DOUBLE;
+	vals2[1].nul = 0;
+	vals2[1].val.double_val = q2double(_c->q);
+
+	vals2[2].type = DB1_INT;
+	vals2[2].nul = 0;
+	vals2[2].val.int_val = _c->cseq;
+
+	vals2[3].type = DB1_INT;
+	vals2[3].nul = 0;
+	vals2[3].val.bitmap_val = _c->flags;
+
+	vals2[4].type = DB1_INT;
+	vals2[4].nul = 0;
+	vals2[4].val.bitmap_val = _c->cflags;
+
+	vals2[5].type = DB1_STR;
+	vals2[5].nul = 0;
+	vals2[5].val.str_val = _c->user_agent;
+
+	vals2[6].type = DB1_STR;
+	if (_c->received.s == 0) {
+		vals2[6].nul = 1;
+	} else {
+		vals2[6].nul = 0;
+		vals2[6].val.str_val = _c->received;
+	}
+	
+	vals2[7].type = DB1_STR;
+	if (_c->path.s == 0) {
+		vals2[7].nul = 1;
+	} else {
+		vals2[7].nul = 0;
+		vals2[7].val.str_val = _c->path;
+	}
+
+	vals2[8].type = DB1_STR;
+	if (_c->sock) {
+		vals2[8].val.str_val = _c->sock->sock_str;
+		vals2[8].nul = 0;
+	} else {
+		vals2[8].nul = 1;
+	}
+
+	vals2[9].type = DB1_BITMAP;
+	if (_c->methods == 0xFFFFFFFF) {
+		vals2[9].nul = 1;
+	} else {
+		vals2[9].val.bitmap_val = _c->methods;
+		vals2[9].nul = 0;
+	}
+
+	vals2[10].type = DB1_DATETIME;
+	vals2[10].nul = 0;
+	vals2[10].val.time_val = _c->last_modified;
+
+	if (use_domain) {
+		vals1[3].type = DB1_STR;
+		vals1[3].nul = 0;
+		dom = memchr(_c->aor->s, '@', _c->aor->len);
+		if (dom==0) {
+			vals1[0].val.str_val.len = 0;
+			vals1[3].val.str_val = *_c->aor;
+		} else {
+			vals1[0].val.str_val.len = dom - _c->aor->s;
+			vals1[3].val.str_val.s = dom + 1;
+			vals1[3].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1;
+		}
+	}
+
+	if (ul_db_layer_update(_d, &vals1[0].val.str_val, &vals1[3].val.str_val, keys1, 0, vals1, keys2, vals2, 
+	(use_domain) ? (4) : (3), 11) < 0) {
+		LM_ERR("updating database failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*!
+ * \brief Delete contact from the database
+ * \param _c deleted contact
+ * \return 0 on success, -1 on failure
+ */
+int db_delete_ucontact(ucontact_t* _c)
+{
+	char* dom;
+	db_key_t keys[4];
+	db_val_t vals[4];
+
+	if (_c->flags & FL_MEM) {
+		return 0;
+	}
+	struct udomain * _d;
+	if(register_udomain(_c->domain->s, &_d) < 0){
+		return -1;
+	}
+
+	keys[0] = &user_col;
+	keys[1] = &contact_col;
+	keys[2] = &callid_col;
+	keys[3] = &domain_col;
+
+	vals[0].type = DB1_STR;
+	vals[0].nul = 0;
+	vals[0].val.str_val = *_c->aor;
+
+	vals[1].type = DB1_STR;
+	vals[1].nul = 0;
+	vals[1].val.str_val = _c->c;
+
+	vals[2].type = DB1_STR;
+	vals[2].nul = 0;
+	vals[2].val.str_val = _c->callid;
+
+	if (use_domain) {
+		vals[3].type = DB1_STR;
+		vals[3].nul = 0;
+		dom = memchr(_c->aor->s, '@', _c->aor->len);
+		if (dom==0) {
+			vals[0].val.str_val.len = 0;
+			vals[3].val.str_val = *_c->aor;
+		} else {
+			vals[0].val.str_val.len = dom - _c->aor->s;
+			vals[3].val.str_val.s = dom + 1;
+			vals[3].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1;
+		}
+	}
+
+	if (ul_db_layer_delete(_d, &vals[0].val.str_val, &vals[3].val.str_val, keys, 0, vals, (use_domain) ? (4) : (3)) < 0) {
+		LM_ERR("deleting from database failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*!
+ * \brief Remove a contact from list belonging to a certain record
+ * \param _r record the contact belongs
+ * \param _c removed contact
+ */
+static inline void unlink_contact(struct urecord* _r, ucontact_t* _c)
+{
+	if (_c->prev) {
+		_c->prev->next = _c->next;
+		if (_c->next) {
+			_c->next->prev = _c->prev;
+		}
+	} else {
+		_r->contacts = _c->next;
+		if (_c->next) {
+			_c->next->prev = 0;
+		}
+	}
+}
+
+
+/*!
+ * \brief Insert a new contact into the list at the correct position
+ * \param _r record that holds the sorted contacts
+ * \param _c new contact
+ */
+static inline void update_contact_pos(struct urecord* _r, ucontact_t* _c)
+{
+	ucontact_t *pos, *ppos;
+
+	if (desc_time_order) {
+		/* order by time - first the newest */
+		if (_c->prev==0)
+			return;
+		unlink_contact(_r, _c);
+		/* insert it at the beginning */
+		_c->next = _r->contacts;
+		_c->prev = 0;
+		_r->contacts->prev = _c;
+		_r->contacts = _c;
+	} else {
+		/* order by q - first the smaller q */
+		if ( (_c->prev==0 || _c->q<=_c->prev->q)
+		&& (_c->next==0 || _c->q>=_c->next->q)  )
+			return;
+		/* need to move , but where? */
+		unlink_contact(_r, _c);
+		_c->next = _c->prev = 0;
+		for(pos=_r->contacts,ppos=0;pos&&pos->q<_c->q;ppos=pos,pos=pos->next);
+		if (pos) {
+			if (!pos->prev) {
+				pos->prev = _c;
+				_c->next = pos;
+				_r->contacts = _c;
+			} else {
+				_c->next = pos;
+				_c->prev = pos->prev;
+				pos->prev->next = _c;
+				pos->prev = _c;
+			}
+		} else if (ppos) {
+			ppos->next = _c;
+			_c->prev = ppos;
+		} else {
+			_r->contacts = _c;
+		}
+	}
+}
+
+
+/*!
+ * \brief Update ucontact with new values
+ * \param _r record the contact belongs to
+ * \param _c updated contact
+ * \param _ci new contact informations
+ * \return 0 on success, -1 on failure
+ */
+int update_ucontact(struct urecord* _r, ucontact_t* _c, ucontact_info_t* _ci)
+{
+	/* we have to update memory in any case, but database directly
+	 * only in db_mode 1 */
+	if (mem_update_ucontact( _c, _ci) < 0) {
+		LM_ERR("failed to update memory\n");
+		return -1;
+	}
+
+	/* run callbacks for UPDATE event */
+	if (exists_ulcb_type(UL_CONTACT_UPDATE))
+	{
+		LM_DBG("exists callback for type= UL_CONTACT_UPDATE\n");
+		run_ul_callbacks( UL_CONTACT_UPDATE, _c);
+	}
+
+	if (_r && db_mode!=DB_ONLY)
+		update_contact_pos( _r, _c);
+
+	st_update_ucontact(_c);
+
+	if (db_mode == WRITE_THROUGH || db_mode==DB_ONLY) {
+		/*
+		 * prevent problems when we're in a failover situation: the first DB contains
+		 * the complete location entries, the other misses some of them. Before the
+		 * update it checks for a entry in the first DB, this is ok. But the update
+		 * in the second DB will not work. Thus the expire mechanism don't work, it
+		 * takes too long until both DBs have the same number of entries again.
+		 */
+		if (db_insert_ucontact(_c) < 0) {
+			LM_ERR("failed to insert_update database\n");
+			return -1;
+		} else {
+			_c->state = CS_SYNC;
+		}
+	}
+	return 0;
+}

+ 219 - 0
modules_k/p_usrloc/ucontact.h

@@ -0,0 +1,219 @@
+/*
+ * $Id: ucontact.h 5272 2008-11-27 12:32:26Z henningw $ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ---------
+ * 2003-03-12 added replication mark and three zombie states (nils)
+ * 2005-07-11 added FL_NAT_SIPPING for nat pinging with SIP method
+ *             instead of UDP package (bogdan)
+ */
+
+/*! \file
+ *  \brief USRLOC - Usrloc contact structure
+ *  \ingroup usrloc
+ */
+
+
+#ifndef UCONTACT_H
+#define UCONTACT_H
+
+
+#include <stdio.h>
+#include <time.h>
+#include "../../qvalue.h"
+#include "../../str.h"
+
+
+/*!
+ * \brief States for in-memory contacts in regards to contact storage handler (db, in-memory, ldap etc)
+ */
+typedef enum cstate {
+	CS_NEW,        /*!< New contact - not flushed yet */
+	CS_SYNC,       /*!< Synchronized contact with the database */
+	CS_DIRTY       /*!< Update contact - not flushed yet */
+} cstate_t;
+
+
+/*! \brief Flags that can be associated with a Contact */
+typedef enum flags {
+	FL_NONE        = 0,          /*!< No flags set */
+	FL_MEM         = 1 << 0,     /*!< Update memory only */
+	FL_ALL         = (int)0xFFFFFFFF  /*!< All flags set */
+} flags_t;
+
+
+/*! \brief Main structure for handling of registered Contact data */
+typedef struct ucontact {
+	str* domain;            /*!< Pointer to domain name (NULL terminated) */
+	str* aor;               /*!< Pointer to the AOR string in record structure*/
+	str c;                  /*!< Contact address */
+	str received;           /*!< IP+port+protocol we received the REGISTER from */
+	str path;               /*!< Path header */
+	time_t expires;         /*!< Expires parameter */
+	qvalue_t q;             /*!< q parameter */
+	str callid;             /*!< Call-ID header field of registration */
+	int cseq;               /*!< CSeq value */
+	cstate_t state;         /*!< State of the contact (\ref cstate) */
+	unsigned int flags;     /*!< Various flags (NAT, ping type, etc) */
+	unsigned int cflags;    /*!< Custom contact flags (from script) */
+	str user_agent;         /*!< User-Agent header field */
+	struct socket_info *sock; /*!< received socket */
+	time_t last_modified;   /*!< When the record was last modified */
+	unsigned int methods;   /*!< Supported methods */
+	struct ucontact* next;  /*!< Next contact in the linked list */
+	struct ucontact* prev;  /*!< Previous contact in the linked list */
+} ucontact_t;
+
+
+/*! \brief Informations related to a contact */
+typedef struct ucontact_info {
+	str received;             /*!< Received interface */
+	str* path;                /*!< Path informations */
+	time_t expires;           /*!< Contact expires */
+	qvalue_t q;               /*!< Q-value */
+	str* callid;              /*!< call-ID */
+	int cseq;                 /*!< CSEQ number */
+	unsigned int flags;       /*!< message flags */
+	unsigned int cflags;      /*!< contact flags */
+	str *user_agent;          /*!< user agent header */
+	struct socket_info *sock; /*!< socket informations */
+	unsigned int methods;     /*!< supported methods */
+	time_t last_modified;     /*!< last modified */
+} ucontact_info_t;
+
+/*! \brief ancient time used for marking the contacts forced to expired */
+#define UL_EXPIRED_TIME 10
+
+/*! \brief Valid contact is a contact that either didn't expire yet or is permanent */
+#define VALID_CONTACT(c, t)   ((c->expires>t) || (c->expires==0))
+
+
+/*!
+ * \brief Create a new contact structure
+ * \param _dom domain
+ * \param _aor address of record
+ * \param _contact contact string
+ * \param _ci contact informations
+ * \return new created contact on success, 0 on failure
+ */
+ucontact_t* new_ucontact(str* _dom, str* _aor, str* _contact,
+		ucontact_info_t* _ci);
+
+
+/*!
+ * \brief Free all memory associated with given contact structure
+ * \param _c freed contact
+ */
+void free_ucontact(ucontact_t* _c);
+
+
+/*!
+ * \brief Print contact, for debugging purposes only
+ * \param _f output file
+ * \param _c printed contact
+ */
+void print_ucontact(FILE* _f, ucontact_t* _c);
+
+
+/*!
+ * \brief Update existing contact in memory with new values
+ * \param _c contact
+ * \param _ci contact informations
+ * \return 0
+ */
+int mem_update_ucontact(ucontact_t* _c, ucontact_info_t *_ci);
+
+
+/* ===== State transition functions - for write back cache scheme ======== */
+
+/*!
+ * \brief Update state of the contact if we are using write-back scheme
+ * \param _c updated contact
+ */
+void st_update_ucontact(ucontact_t* _c);
+
+
+/*!
+ * \brief Update state of the contact
+ * \param _c updated contact
+ * \return 1 if the contact should be deleted from memory immediately, 0 otherwise
+ */
+int st_delete_ucontact(ucontact_t* _c);
+
+
+/*!
+ * \brief Called when the timer is about to delete an expired contact
+ * \param _c expired contact
+ * \return 1 if the contact should be removed from the database and 0 otherwise
+ */
+int st_expired_ucontact(ucontact_t* _c);
+
+
+/*!
+ * \brief Called when the timer is about flushing the contact, updates contact state
+ * \param _c flushed contact
+ * \return 1 if the contact should be inserted, 2 if update and 0 otherwise
+ */
+int st_flush_ucontact(ucontact_t* _c);
+
+
+/* ==== Database related functions ====== */
+
+/*!
+ * \brief Insert contact into the database
+ * \param _c inserted contact
+ * \return 0 on success, -1 on failure
+ */
+int db_insert_ucontact(ucontact_t* _c);
+
+
+/*!
+ * \brief Update contact in the database
+ * \param _c updated contact
+ * \return 0 on success, -1 on failure
+ */
+int db_update_ucontact(ucontact_t* _c);
+
+
+/*!
+ * \brief Delete contact from the database
+ * \param _c deleted contact
+ * \return 0 on success, -1 on failure
+ */
+int db_delete_ucontact(ucontact_t* _c);
+
+
+/* ====== Module interface ====== */
+
+struct urecord;
+
+/*!
+ * \brief Update ucontact with new values
+ * \param _r record the contact belongs to
+ * \param _c updated contact
+ * \param _ci new contact informations
+ * \return 0 on success, -1 on failure
+ */
+typedef int (*update_ucontact_t)(struct urecord* _r, ucontact_t* _c,
+		ucontact_info_t* _ci);
+int update_ucontact(struct urecord* _r, ucontact_t* _c, ucontact_info_t* _ci);
+
+#endif

+ 717 - 0
modules_k/p_usrloc/udomain.c

@@ -0,0 +1,717 @@
+/*
+ * $Id: udomain.c 5272 2008-11-27 12:32:26Z henningw $ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ---------
+ * 2003-03-11 changed to the new locking scheme: locking.h (andrei)
+ * 2003-03-12 added replication mark and zombie state (nils)
+ * 2004-06-07 updated to the new DB api (andrei)
+ * 2004-08-23  hash function changed to process characters as unsigned
+ *             -> no negative results occur (jku)
+ */
+
+/*! \file
+ *  \brief USRLOC - Userloc domain handling functions
+ *  \ingroup usrloc
+ *
+ * - Module: \ref usrloc
+ */
+
+#include "udomain.h"
+#include <string.h>
+#include "../../parser/parse_methods.h"
+#include "../../mem/shm_mem.h"
+#include "../../dprint.h"
+#include "../../lib/srdb1/db.h"
+#include "../../socket_info.h"
+#include "../../ut.h"
+#include "../../lib/kcore/hash_func.h"
+#include "ul_mod.h"            /* usrloc module parameters */
+#include "utime.h"
+#include "ul_db_layer.h"
+
+
+#ifdef STATISTICS
+static char *build_stat_name( str* domain, char *var_name)
+{
+	int n;
+	char *s;
+	char *p;
+
+	n = domain->len + 1 + strlen(var_name) + 1;
+	s = (char*)shm_malloc( n );
+	if (s==0) {
+		LM_ERR("no more shm mem\n");
+		return 0;
+	}
+	memcpy( s, domain->s, domain->len);
+	p = s + domain->len;
+	*(p++) = '-';
+	memcpy( p , var_name, strlen(var_name));
+	p += strlen(var_name);
+	*(p++) = 0;
+	return s;
+}
+#endif
+
+
+/*!
+ * \brief Create a new domain structure
+ * \param  _n is pointer to str representing name of the domain, the string is
+ * not copied, it should point to str structure stored in domain list
+ * \param _s is hash table size
+ * \param _d new created domain
+ * \return 0 on success, -1 on failure
+ */
+int new_udomain(str* _n, int _s, udomain_t** _d)
+{
+	int i;
+#ifdef STATISTICS
+	char *name;
+#endif
+	
+	/* Must be always in shared memory, since
+	 * the cache is accessed from timer which
+	 * lives in a separate process
+	 */
+	*_d = (udomain_t*)shm_malloc(sizeof(udomain_t));
+	if (!(*_d)) {
+		LM_ERR("new_udomain(): No memory left\n");
+		goto error0;
+	}
+	memset(*_d, 0, sizeof(udomain_t));
+	
+	(*_d)->table = (hslot_t*)shm_malloc(sizeof(hslot_t) * _s);
+	if (!(*_d)->table) {
+		LM_ERR("no memory left 2\n");
+		goto error1;
+	}
+
+	(*_d)->name = _n;
+	
+	for(i = 0; i < _s; i++) {
+		init_slot(*_d, &((*_d)->table[i]), i);
+	}
+
+	(*_d)->size = _s;
+
+#ifdef STATISTICS
+	/* register the statistics */
+	if ( (name=build_stat_name(_n,"users"))==0 || register_stat("usrloc",
+	name, &(*_d)->users, STAT_NO_RESET|STAT_SHM_NAME)!=0 ) {
+		LM_ERR("failed to add stat variable\n");
+		goto error2;
+	}
+	if ( (name=build_stat_name(_n,"contacts"))==0 || register_stat("usrloc",
+	name, &(*_d)->contacts, STAT_NO_RESET|STAT_SHM_NAME)!=0 ) {
+		LM_ERR("failed to add stat variable\n");
+		goto error2;
+	}
+	if ( (name=build_stat_name(_n,"expires"))==0 || register_stat("usrloc",
+	name, &(*_d)->expires, STAT_SHM_NAME)!=0 ) {
+		LM_ERR("failed to add stat variable\n");
+		goto error2;
+	}
+#endif
+
+	return 0;
+error2:
+	shm_free((*_d)->table);
+error1:
+	shm_free(*_d);
+error0:
+	return -1;
+}
+
+
+/*!
+ * \brief Free all memory allocated for the domain
+ * \param _d freed domain
+ */
+void free_udomain(udomain_t* _d)
+{
+	int i;
+	
+	if (_d->table) {
+		for(i = 0; i < _d->size; i++) {
+			lock_ulslot(_d, i);
+			deinit_slot(_d->table + i);
+			unlock_ulslot(_d, i);
+		}
+		shm_free(_d->table);
+	}
+	shm_free(_d);
+}
+
+
+/*!
+ * \brief Returns a static dummy urecord for temporary usage
+ * \param _d domain (needed for the name)
+ * \param _aor address of record
+ * \param _r new created urecord
+ */
+static inline void get_static_urecord(udomain_t* _d, str* _aor,
+														struct urecord** _r)
+{
+	static struct urecord r;
+
+	memset( &r, 0, sizeof(struct urecord) );
+	r.aor = *_aor;
+	r.domain = _d->name;
+	*_r = &r;
+}
+
+
+/*!
+ * \brief Debugging helper function
+ */
+void print_udomain(FILE* _f, udomain_t* _d)
+{
+	int i;
+	int max=0, slot=0, n=0;
+	struct urecord* r;
+	fprintf(_f, "---Domain---\n");
+	fprintf(_f, "name : '%.*s'\n", _d->name->len, ZSW(_d->name->s));
+	fprintf(_f, "size : %d\n", _d->size);
+	fprintf(_f, "table: %p\n", _d->table);
+	/*fprintf(_f, "lock : %d\n", _d->lock); -- can be a structure --andrei*/
+	fprintf(_f, "\n");
+	for(i=0; i<_d->size; i++)
+	{
+		r = _d->table[i].first;
+		n += _d->table[i].n;
+		if(max<_d->table[i].n){
+			max= _d->table[i].n;
+			slot = i;
+		}
+		while(r) {
+			print_urecord(_f, r);
+			r = r->next;
+		}
+	}
+	fprintf(_f, "\nMax slot: %d (%d/%d)\n", max, slot, n);
+	fprintf(_f, "\n---/Domain---\n");
+}
+
+
+/*!
+ * \brief Convert database values into ucontact_info
+ *
+ * Convert database values into ucontact_info, 
+ * expects 12 rows (contact, expirs, q, callid, cseq, flags,
+ * ua, received, path, socket, methods, last_modified)
+ * \param vals database values
+ * \param contact contact
+ * \return pointer to the ucontact_info on success, 0 on failure
+ */
+static inline ucontact_info_t* dbrow2info( db_val_t *vals, str *contact)
+{
+	static ucontact_info_t ci;
+	static str callid, ua, received, host, path;
+	int port, proto;
+	char *p;
+
+	memset( &ci, 0, sizeof(ucontact_info_t));
+
+	contact->s = (char*)VAL_STRING(vals);
+	if (VAL_NULL(vals) || contact->s==0 || contact->s[0]==0) {
+		LM_CRIT("bad contact\n");
+		return 0;
+	}
+	contact->len = strlen(contact->s);
+
+	if (VAL_NULL(vals+1)) {
+		LM_CRIT("empty expire\n");
+		return 0;
+	}
+	ci.expires = VAL_TIME(vals+1);
+
+	if (VAL_NULL(vals+2)) {
+		LM_CRIT("empty q\n");
+		return 0;
+	}
+	ci.q = double2q(VAL_DOUBLE(vals+2));
+
+	if (VAL_NULL(vals+4)) {
+		LM_CRIT("empty cseq_nr\n");
+		return 0;
+	}
+	ci.cseq = VAL_INT(vals+4);
+
+	callid.s = (char*)VAL_STRING(vals+3);
+	if (VAL_NULL(vals+3) || !callid.s || !callid.s[0]) {
+		LM_CRIT("bad callid\n");
+		return 0;
+	}
+	callid.len  = strlen(callid.s);
+	ci.callid = &callid;
+
+	if (VAL_NULL(vals+5)) {
+		LM_CRIT("empty flag\n");
+		return 0;
+	}
+	ci.flags  = VAL_BITMAP(vals+5);
+
+	if (VAL_NULL(vals+6)) {
+		LM_CRIT("empty cflag\n");
+		return 0;
+	}
+	ci.cflags  = VAL_BITMAP(vals+6);
+
+	ua.s  = (char*)VAL_STRING(vals+7);
+	if (VAL_NULL(vals+7) || !ua.s || !ua.s[0]) {
+		ua.s = 0;
+		ua.len = 0;
+	} else {
+		ua.len = strlen(ua.s);
+	}
+	ci.user_agent = &ua;
+
+	received.s  = (char*)VAL_STRING(vals+8);
+	if (VAL_NULL(vals+8) || !received.s || !received.s[0]) {
+		received.len = 0;
+		received.s = 0;
+	} else {
+		received.len = strlen(received.s);
+	}
+	ci.received = received;
+	
+	path.s  = (char*)VAL_STRING(vals+9);
+		if (VAL_NULL(vals+9) || !path.s || !path.s[0]) {
+			path.len = 0;
+			path.s = 0;
+		} else {
+			path.len = strlen(path.s);
+		}
+	ci.path= &path;
+
+	/* socket name */
+	p  = (char*)VAL_STRING(vals+10);
+	if (VAL_NULL(vals+10) || p==0 || p[0]==0){
+		ci.sock = 0;
+	} else {
+		if (parse_phostport( p, &host.s, &host.len,
+				&port, &proto)!=0){
+			LM_ERR("bad socket <%s>\n", p);
+			return 0;
+		}
+		ci.sock = grep_sock_info( &host, (unsigned short)port, proto);
+		if (ci.sock==0) {
+			LM_INFO("non-local socket <%s>...ignoring\n", p);
+		}
+	}
+
+	/* supported methods */
+	if (VAL_NULL(vals+11)) {
+		ci.methods = ALL_METHODS;
+	} else {
+		ci.methods = VAL_BITMAP(vals+11);
+	}
+
+	/* last modified time */
+	if (!VAL_NULL(vals+12)) {
+		ci.last_modified = VAL_TIME(vals+12);
+	}
+
+	return &ci;
+}
+
+
+
+/*!
+ * \brief Loads from DB all contacts for an AOR
+ * \param _c database connection
+ * \param _d domain
+ * \param _aor address of record
+ * \return pointer to the record on success, 0 on errors or if nothing is found
+ */
+urecord_t* db_load_urecord(udomain_t* _d, str *_aor)
+{
+	ucontact_info_t *ci;
+	db_key_t columns[13];
+	db_key_t keys[2];
+	db_key_t order;
+	db_val_t vals[2];
+	db1_res_t* res = NULL;
+	str contact;
+	char *domain;
+	int i;
+
+	urecord_t* r;
+	ucontact_t* c;
+
+	keys[0] = &user_col;
+	vals[0].type = DB1_STR;
+	vals[0].nul = 0;
+	if (use_domain) {
+		keys[1] = &domain_col;
+		vals[1].type = DB1_STR;
+		vals[1].nul = 0;
+		domain = memchr(_aor->s, '@', _aor->len);
+		vals[0].val.str_val.s   = _aor->s;
+		if (domain==0) {
+			vals[0].val.str_val.len = 0;
+			vals[1].val.str_val = *_aor;
+		} else {
+			vals[0].val.str_val.len = domain - _aor->s;
+			vals[1].val.str_val.s   = domain+1;
+			vals[1].val.str_val.len = _aor->s + _aor->len - domain - 1;
+		}
+	} else {
+		vals[0].val.str_val = *_aor;
+	}
+
+	columns[0] = &contact_col;
+	columns[1] = &expires_col;
+	columns[2] = &q_col;
+	columns[3] = &callid_col;
+	columns[4] = &cseq_col;
+	columns[5] = &flags_col;
+	columns[6] = &cflags_col;
+	columns[7] = &user_agent_col;
+	columns[8] = &received_col;
+	columns[9] = &path_col;
+	columns[10] = &sock_col;
+	columns[11] = &methods_col;
+	columns[12] = &last_mod_col;
+
+	if (desc_time_order)
+		order = &last_mod_col;
+	else
+		order = &q_col;
+
+	LM_ERR("Calluing ul_db_layer_query\n");
+	if (ul_db_layer_query(_d,  &vals[0].val.str_val,  &vals[1].val.str_val, keys, 0, vals, columns, (use_domain)?2:1, 13, order,
+				&res) < 0) {
+		LM_ERR("db_query failed\n");
+		return 0;
+	}
+
+	if (RES_ROW_N(res) == 0) {
+		LM_DBG("aor %.*s not found in table %.*s\n",_aor->len, _aor->s, _d->name->len, _d->name->s);
+
+		ul_db_layer_free_result(_d, res);
+		return 0;
+	}
+
+	r = 0;
+
+	for(i = 0; i < RES_ROW_N(res); i++) {
+		ci = dbrow2info(  ROW_VALUES(RES_ROWS(res) + i), &contact);
+		if (ci==0) {
+			LM_ERR("skipping record for %.*s in table %s\n",
+					_aor->len, _aor->s, _d->name->s);
+			continue;
+		}
+		
+		if ( r==0 )
+			get_static_urecord( _d, _aor, &r);
+
+		if ( (c=mem_insert_ucontact(r, &contact, ci)) == 0) {
+			LM_ERR("mem_insert failed\n");
+			free_urecord(r);
+			ul_db_layer_free_result(_d, res);
+			return 0;
+		}
+
+		/* We have to do this, because insert_ucontact sets state to CS_NEW
+		 * and we have the contact in the database already */
+		c->state = CS_SYNC;
+	}
+
+	ul_db_layer_free_result(_d, res);
+	return r;
+}
+
+
+/*!
+ * \brief Timer function to cleanup expired contacts, DB_ONLY db_mode
+ * \param _d cleaned domain
+ * \return 0 on success, -1 on failure
+ */
+int db_timer_udomain(udomain_t* _d)
+{
+	db_key_t keys[2];
+	db_op_t  ops[2];
+	db_val_t vals[2];
+
+	keys[0] = &expires_col;
+	ops[0] = "<";
+	vals[0].type = DB1_DATETIME;
+	vals[0].nul = 0;
+	vals[0].val.time_val = act_time + 1;
+
+	keys[1] = &expires_col;
+	ops[1] = "!=";
+	vals[1].type = DB1_DATETIME;
+	vals[1].nul = 0;
+	vals[1].val.time_val = 0;
+	// we're using a local cron job to delete expired entries
+	LM_INFO("using sp-ul_db database interface, expires is not implemented");
+	//if (ul_db_layer_delete(_d, NULL, NULL, keys, ops, vals, 2) < 0) { //FIXME
+	return 0;
+}
+
+
+
+/*!
+ * \brief Insert a new record into domain in memory
+ * \param _d domain the record belongs to
+ * \param _aor address of record
+ * \param _r new created record
+ * \return 0 on success, -1 on failure
+ */
+int mem_insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
+{
+	int sl;
+	
+	if (new_urecord(_d->name, _aor, _r) < 0) {
+		LM_ERR("creating urecord failed\n");
+		return -1;
+	}
+
+	sl = ((*_r)->aorhash)&(_d->size-1);
+	slot_add(&_d->table[sl], *_r);
+	update_stat( _d->users, 1);
+	return 0;
+}
+
+
+/*!
+ * \brief Remove a record from domain in memory
+ * \param _d domain the record belongs to
+ * \param _r deleted record
+ */
+void mem_delete_urecord(udomain_t* _d, struct urecord* _r)
+{
+	slot_rem(_r->slot, _r);
+	free_urecord(_r);
+	update_stat( _d->users, -1);
+}
+
+
+/*!
+ * \brief Run timer handler for given domain
+ * \param _d domain
+ */
+void mem_timer_udomain(udomain_t* _d)
+{
+	struct urecord* ptr, *t;
+	int i;
+
+	for(i=0; i<_d->size; i++)
+	{
+		lock_ulslot(_d, i);
+
+		ptr = _d->table[i].first;
+
+		while(ptr) {
+			timer_urecord(ptr);
+			/* Remove the entire record if it is empty */
+			if (ptr->contacts == 0) {
+				t = ptr;
+				ptr = ptr->next;
+				mem_delete_urecord(_d, t);
+			} else {
+				ptr = ptr->next;
+			}
+		}
+		unlock_ulslot(_d, i);
+	}
+}
+
+
+/*!
+ * \brief Get lock for a domain
+ * \param _d domain
+ * \param _aor adress of record, used as hash source for the lock slot
+ */
+void lock_udomain(udomain_t* _d, str* _aor)
+{
+	unsigned int sl;
+	if (db_mode!=DB_ONLY)
+	{
+		sl = core_hash(_aor, 0, _d->size);
+
+#ifdef GEN_LOCK_T_PREFERED
+		lock_get(_d->table[sl].lock);
+#else
+		ul_lock_idx(_d->table[sl].lockidx);
+#endif
+	}
+}
+
+
+/*!
+ * \brief Release lock for a domain
+ * \param _d domain
+ * \param _aor address of record, uses as hash source for the lock slot
+ */
+void unlock_udomain(udomain_t* _d, str* _aor)
+{
+	unsigned int sl;
+	if (db_mode!=DB_ONLY)
+	{
+		sl = core_hash(_aor, 0, _d->size);
+#ifdef GEN_LOCK_T_PREFERED
+		lock_release(_d->table[sl].lock);
+#else
+		ul_release_idx(_d->table[sl].lockidx);
+#endif
+	}
+}
+
+/*!
+ * \brief  Get lock for a slot
+ * \param _d domain
+ * \param i slot number
+ */
+void lock_ulslot(udomain_t* _d, int i)
+{
+	if (db_mode!=DB_ONLY)
+#ifdef GEN_LOCK_T_PREFERED
+		lock_get(_d->table[i].lock);
+#else
+		ul_lock_idx(_d->table[i].lockidx);
+#endif
+}
+
+
+/*!
+ * \brief Release lock for a slot
+ * \param _d domain
+ * \param i slot number
+ */
+void unlock_ulslot(udomain_t* _d, int i)
+{
+	if (db_mode!=DB_ONLY)
+#ifdef GEN_LOCK_T_PREFERED
+		lock_release(_d->table[i].lock);
+#else
+		ul_release_idx(_d->table[i].lockidx);
+#endif
+}
+
+
+
+/*!
+ * \brief Create and insert a new record
+ * \param _d domain to insert the new record
+ * \param _aor address of the record
+ * \param _r new created record
+ * \return return 0 on success, -1 on failure
+ */
+int insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
+{
+	if (db_mode!=DB_ONLY) {
+		if (mem_insert_urecord(_d, _aor, _r) < 0) {
+			LM_ERR("inserting record failed\n");
+			return -1;
+		}
+	} else {
+		get_static_urecord( _d, _aor, _r);
+	}
+	return 0;
+}
+
+
+/*!
+ * \brief Obtain a urecord pointer if the urecord exists in domain
+ * \param _d domain to search the record
+ * \param _aor address of record
+ * \param _r new created record
+ * \return 0 if a record was found, 1 if nothing could be found
+ */
+int get_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
+{
+	LM_ERR("get_urecord: aor: %.*s", _aor->len, _aor->s);
+	unsigned int sl, i, aorhash;
+	urecord_t* r;
+	LM_ERR("called get urecord for %.*s, mode is %d", _aor->len, _aor->s, db_mode);
+	if (db_mode!=DB_ONLY) {
+		/* search in cache */
+		aorhash = core_hash(_aor, 0, 0);
+		sl = aorhash&(_d->size-1);
+		r = _d->table[sl].first;
+
+		for(i = 0; i < _d->table[sl].n; i++) {
+			if((r->aorhash==aorhash) && (r->aor.len==_aor->len)
+						&& !memcmp(r->aor.s,_aor->s,_aor->len)){
+				*_r = r;
+				return 0;
+			}
+
+			r = r->next;
+		}
+	} else {
+		/* search in DB */
+		LM_ERR("called get urecord for %.*s", _aor->len, _aor->s);
+		r = db_load_urecord(_d, _aor);
+		if (r) {
+			*_r = r;
+			return 0;
+		}
+	}
+
+	return 1;   /* Nothing found */
+}
+
+
+/*!
+ * \brief Delete a urecord from domain
+ * \param _d domain where the record should be deleted
+ * \param _aor address of record
+ * \param _r deleted record
+ * \return 0 on success, -1 if the record could not be deleted
+ */
+int delete_urecord(udomain_t* _d, str* _aor, struct urecord* _r)
+{
+	struct ucontact* c, *t;
+
+	if (db_mode==DB_ONLY) {
+		if (_r==0)
+			get_static_urecord( _d, _aor, &_r);
+		if (db_delete_urecord(_d, _r)<0) {
+			LM_ERR("DB delete failed\n");
+			return -1;
+		}
+		free_urecord(_r);
+		return 0;
+	}
+
+	if (_r==0) {
+		if (get_urecord(_d, _aor, &_r) > 0) {
+			return 0;
+		}
+	}
+
+	c = _r->contacts;
+	while(c) {
+		t = c;
+		c = c->next;
+		if (delete_ucontact(_r, t) < 0) {
+			LM_ERR("deleting contact failed\n");
+			return -1;
+		}
+	}
+	release_urecord(_r);
+	return 0;
+}

+ 194 - 0
modules_k/p_usrloc/udomain.h

@@ -0,0 +1,194 @@
+/*
+ * $Id: udomain.h 5272 2008-11-27 12:32:26Z henningw $ 
+
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+/*
+ * History:
+ * --------
+ *  2003-03-11  changed to new locking scheme: locking.h (andrei)
+ */
+
+
+/*! \file
+ *  \brief USRLOC - Usrloc domain structure
+ *  \ingroup usrloc
+ */
+
+#ifndef UDOMAIN_H
+#define UDOMAIN_H
+
+
+#include <stdio.h>
+#include "../../lib/kcore/statistics.h"
+#include "../../locking.h"
+#include "../../str.h"
+#include "../../lib/srdb1/db.h"
+#include "urecord.h"
+#include "hslot.h"
+
+
+struct hslot;   /*!< Hash table slot */
+struct urecord; /*!< Usrloc record */
+
+
+/*! \brief
+ * The structure represents a usrloc domain
+ */
+typedef struct udomain {
+	str* name;                 /*!< Domain name (NULL terminated) */
+	int size;                  /*!< Hash table size */
+	struct hslot* table;       /*!< Hash table - array of collision slots */
+	/* statistics */
+	stat_var *users;           /*!< no of registered users */
+	stat_var *contacts;        /*!< no of registered contacts */
+	stat_var *expires;         /*!< no of expires */
+	/* for ul_db_layer */
+	int dbt;                   /* type of the database */
+	db1_con_t * dbh;            /* database handle */
+} udomain_t;
+
+
+/*!
+ * \brief Create a new domain structure
+ * \param  _n is pointer to str representing name of the domain, the string is
+ * not copied, it should point to str structure stored in domain list
+ * \param _s is hash table size
+ * \param _d new created domain
+ * \return 0 on success, -1 on failure
+ */
+int new_udomain(str* _n, int _s, udomain_t** _d);
+
+
+/*!
+ * \brief Free all memory allocated for the domain
+ * \param _d freed domain
+ */
+void free_udomain(udomain_t* _d);
+
+
+/*!
+ * \brief Print udomain, debugging helper function
+ */
+void print_udomain(FILE* _f, udomain_t* _d);
+
+
+
+
+
+/*!
+ * \brief Timer function to cleanup expired contacts, DB_ONLY db_mode
+ * \param _d cleaned domain
+ * \return 0 on success, -1 on failure
+ */
+int db_timer_udomain(udomain_t* _d);
+
+
+/*!
+ * \brief Run timer handler for given domain
+ * \param _d domain
+ */
+void mem_timer_udomain(udomain_t* _d);
+
+
+/*!
+ * \brief Insert a new record into domain in memory
+ * \param _d domain the record belongs to
+ * \param _aor address of record
+ * \param _r new created record
+ * \return 0 on success, -1 on failure
+ */
+int mem_insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r);
+
+
+/*!
+ * \brief Remove a record from domain in memory
+ * \param _d domain the record belongs to
+ * \param _r deleted record
+ */
+void mem_delete_urecord(udomain_t* _d, struct urecord* _r);
+
+
+/*! \brief
+ * Timer handler for given domain
+ */
+typedef void (*lock_udomain_t)(udomain_t* _d, str *_aor);
+void lock_udomain(udomain_t* _d, str *_aor);
+
+
+/*!
+ * \brief Release lock for a domain
+ * \param _d domain
+ * \param _aor address of record, uses as hash source for the lock slot
+ */
+typedef void (*unlock_udomain_t)(udomain_t* _d, str *_aor);
+void unlock_udomain(udomain_t* _d, str *_aor);
+
+
+/*!
+ * \brief  Get lock for a slot
+ * \param _d domain
+ * \param i slot number
+ */
+void lock_ulslot(udomain_t* _d, int i);
+
+/*!
+ * \brief Release lock for a slot
+ * \param _d domain
+ * \param i slot number
+ */
+void unlock_ulslot(udomain_t* _d, int i);
+
+/* ===== module interface ======= */
+
+
+/*!
+ * \brief Create and insert a new record
+ * \param _d domain to insert the new record
+ * \param _aor address of the record
+ * \param _r new created record
+ * \return return 0 on success, -1 on failure
+ */
+typedef int (*insert_urecord_t)(udomain_t* _d, str* _aor, struct urecord** _r);
+int insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r);
+
+
+/*!
+ * \brief Obtain a urecord pointer if the urecord exists in domain
+ * \param _d domain to search the record
+ * \param _aor address of record
+ * \param _r new created record
+ * \return 0 if a record was found, 1 if nothing could be found
+ */
+typedef int  (*get_urecord_t)(udomain_t* _d, str* _aor, struct urecord** _r);
+int get_urecord(udomain_t* _d, str* _aor, struct urecord** _r);
+
+
+/*!
+ * \brief Delete a urecord from domain
+ * \param _d domain where the record should be deleted
+ * \param _aor address of record
+ * \param _r deleted record
+ * \return 0 on success, -1 if the record could not be deleted
+ */
+typedef int  (*delete_urecord_t)(udomain_t* _d, str* _aor, struct urecord* _r);
+int delete_urecord(udomain_t* _d, str* _aor, struct urecord* _r);
+
+
+#endif

+ 121 - 0
modules_k/p_usrloc/ul_callback.c

@@ -0,0 +1,121 @@
+/*
+ * $Id: ul_callback.c 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * --------
+ *  2004-03-16  created (bogdan)
+ */
+
+/*! \file
+ *  \brief USRLOC - Callback functions
+ *  \ingroup usrloc
+ *
+ * - Module: \ref usrloc
+ */
+
+
+#include <stdlib.h>
+
+#include "../../dprint.h"
+#include "../../error.h"
+#include "../../mem/shm_mem.h"
+#include "ul_callback.h"
+
+
+struct ulcb_head_list* ulcb_list = 0;
+
+
+
+int init_ulcb_list(void)
+{
+	ulcb_list = (struct ulcb_head_list*)shm_malloc
+		( sizeof(struct ulcb_head_list) );
+	if (ulcb_list==0) {
+		LM_CRIT("no more shared mem\n");
+		return -1;
+	}
+	ulcb_list->first = 0;
+	ulcb_list->reg_types = 0;
+	return 1;
+}
+
+
+void destroy_ulcb_list(void)
+{
+	struct ul_callback *cbp, *cbp_tmp;
+
+	if (!ulcb_list)
+		return;
+
+	for( cbp=ulcb_list->first; cbp ; ) {
+		cbp_tmp = cbp;
+		cbp = cbp->next;
+		if (cbp_tmp->param) shm_free( cbp_tmp->param );
+		shm_free( cbp_tmp );
+	}
+
+	shm_free(ulcb_list);
+}
+
+
+
+/*! \brief 
+	register a callback function 'f' for 'types' mask of events;
+*/
+int register_ulcb( int types, ul_cb f, void *param )
+{
+	struct ul_callback *cbp;
+
+	/* are the callback types valid?... */
+	if ( types<0 || types>ULCB_MAX ) {
+		LM_CRIT("invalid callback types: mask=%d\n",types);
+		return E_BUG;
+	}
+	/* we don't register null functions */
+	if (f==0) {
+		LM_CRIT("null callback function\n");
+		return E_BUG;
+	}
+
+	/* build a new callback structure */
+	if (!(cbp=(struct ul_callback*)shm_malloc(sizeof( struct ul_callback)))) {
+		LM_ERR("no more share mem\n");
+		return E_OUT_OF_MEM;
+	}
+
+	/* link it into the proper place... */
+	cbp->next = ulcb_list->first;
+	ulcb_list->first = cbp;
+	ulcb_list->reg_types |= types;
+	/* ... and fill it up */
+	cbp->callback = f;
+	cbp->param = param;
+	cbp->types = types;
+	if (cbp->next)
+		cbp->id = cbp->next->id+1;
+	else
+		cbp->id = 0;
+
+	return 1;
+}
+
+
+

+ 95 - 0
modules_k/p_usrloc/ul_callback.h

@@ -0,0 +1,95 @@
+/*
+ * $Id: ul_callback.h 5003 2008-09-26 11:01:51Z henningw $
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * --------
+ *  2004-03-16  created (bogdan)
+ */
+
+/*! \file
+ *  \brief USRLOC - Module callbacks
+ *  \ingroup usrloc
+ */
+
+#ifndef _UL_CALLBACKS_H
+#define _UL_CALLBACKS_H
+
+#include "ucontact.h"
+
+
+#define UL_CONTACT_INSERT      (1<<0)
+#define UL_CONTACT_UPDATE      (1<<1)
+#define UL_CONTACT_DELETE      (1<<2)
+#define UL_CONTACT_EXPIRE      (1<<3)
+#define ULCB_MAX               ((1<<4)-1)
+
+/*! \brief callback function prototype */
+typedef void (ul_cb) (ucontact_t *c, int type, void *param);
+/*! \brief register callback function prototype */
+typedef int (*register_ulcb_t)( int cb_types, ul_cb f, void *param);
+
+
+struct ul_callback {
+	int id;                      /*!< id of this callback - useless */
+	int types;                   /*!< types of events that trigger the callback*/
+	ul_cb* callback;             /*!< callback function */
+	void *param;                 /*!< param to be passed to callback function */
+	struct ul_callback* next;
+};
+
+struct ulcb_head_list {
+	struct ul_callback *first;
+	int reg_types;
+};
+
+
+extern struct ulcb_head_list*  ulcb_list;
+
+
+#define exists_ulcb_type(_types_) \
+	( (ulcb_list->reg_types)|(_types_) )
+
+
+int init_ulcb_list(void);
+
+void destroy_ulcb_list(void);
+
+
+/*! \brief register a callback for several types of events */
+int register_ulcb( int types, ul_cb f, void *param );
+
+/*! \brief run all transaction callbacks for an event type */
+static inline void run_ul_callbacks( int type , ucontact_t *c)
+{
+	struct ul_callback *cbp;
+
+	for (cbp=ulcb_list->first; cbp; cbp=cbp->next)  {
+		if(cbp->types&type) {
+			LM_DBG("contact=%p, callback type %d/%d, id %d entered\n",
+				c, type, cbp->types, cbp->id );
+			cbp->callback( c, type, cbp->param );
+		}
+	}
+}
+
+
+
+#endif

+ 196 - 0
modules_k/p_usrloc/ul_check.c

@@ -0,0 +1,196 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "../../mem/shm_mem.h"
+#include "ul_check.h"
+#include "time.h"
+
+static struct check_list_head * head = NULL;
+
+static struct check_list_element * initialise_element(void);
+
+static void destroy_element(struct check_list_element * element);
+
+int init_list(void) {
+	if(!head){
+		if((head = (struct check_list_head *)shm_malloc(sizeof(struct check_list_head))) == NULL){
+			LM_ERR("couldn't allocate shared memory.\n");
+			return -1;
+		}
+	}
+	memset(head, 0, sizeof(struct check_list_head));
+	
+	if(lock_init(&head->list_lock) == 0){
+		LM_ERR("cannot initialise lock.\n");
+		shm_free(head);
+		return -1;
+	}
+	return 0;
+}
+
+struct check_data * get_new_element(void) {
+	struct check_list_element * ret;
+	if(!head){
+		LM_ERR("list not initialised.\n");
+		return NULL;
+	}
+	LM_DBG("start.\n");
+	lock_get(&head->list_lock);
+	
+	if((ret = initialise_element()) == NULL){
+		lock_release(&head->list_lock);
+		return NULL;
+	}
+	head->element_count++;
+	if(head->first == NULL){
+		LM_DBG("new element is the first.\n");
+		LM_DBG("element_count: %i\n", head->element_count);
+		head->first = ret;
+		lock_release(&head->list_lock);
+		return ret->data;
+	}
+	LM_DBG("new element.\n");
+	LM_DBG("element_count: %i\n", head->element_count);
+	ret->next = head->first;
+	head->first = ret;
+	lock_release(&head->list_lock);
+	return ret->data;
+}
+
+int must_refresh(struct check_data * element) {
+	int ret;
+	lock_get(&element->flag_lock);
+	ret = element->refresh_flag;
+	LM_DBG("refresh_flag is at %i.\n", ret);
+	element->refresh_flag = 0;
+	lock_release(&element->flag_lock);
+	return ret;
+}
+
+int must_reconnect(struct check_data * element) {
+	int ret;
+	lock_get(&element->flag_lock);
+	ret = element->reconnect_flag;
+	LM_DBG("reconnect_flag is at %i.\n", ret);
+	element->reconnect_flag = 0;
+	lock_release(&element->flag_lock);
+	return ret;
+}
+
+int set_must_refresh(void) {
+	struct check_list_element * tmp;
+	int i = 0;
+	lock_get(&head->list_lock);
+	tmp = head->first;
+	while(tmp){
+		lock_get(&tmp->data->flag_lock);
+		tmp->data->refresh_flag = 1;
+		lock_release(&tmp->data->flag_lock);
+		tmp = tmp->next;
+		i++;
+		LM_DBG("element no %i.\n", i);
+	}
+	lock_release(&head->list_lock);
+	return i;
+}
+
+int set_must_reconnect(void) {
+	struct check_list_element * tmp;
+	int i = 0;
+	lock_get(&head->list_lock);
+	tmp = head->first;
+	while(tmp){
+		lock_get(&tmp->data->flag_lock);
+		tmp->data->reconnect_flag = 1;
+		lock_release(&tmp->data->flag_lock);
+		tmp = tmp->next;
+		i++;
+		LM_DBG("element no %i.\n", i);
+	}
+	lock_release(&head->list_lock);
+	return i;
+}
+
+
+int must_retry(time_t * timer, time_t interval){
+	if(!timer){
+		return -1;
+	}
+	LM_DBG("must_retry: time is at %i, retry at %i.\n", (int)time(NULL), (int)(*timer));
+	if(*timer <= time(NULL)){
+		*timer = time(NULL) + interval;
+		return 1;
+	}
+	return 0;
+}
+
+void destroy_list(void) {
+	struct check_list_element * tmp;
+	struct check_list_element * del;
+	if(head){
+		tmp = head->first;
+		while(tmp){
+			del = tmp;
+			tmp = tmp->next;
+			destroy_element(del);
+		}
+		lock_destroy(&head->list_lock);
+		shm_free(head);
+	}
+	return;
+}
+
+static struct check_list_element * initialise_element(void){
+	struct check_list_element * ret;
+	if((ret = (struct check_list_element *)shm_malloc(sizeof(struct check_list_element))) == NULL){
+		LM_ERR("couldn't allocate shared memory.\n");
+		return NULL;
+	}
+	memset(ret, 0, sizeof(struct check_list_element));
+	
+	if((ret->data = (struct check_data *)shm_malloc(sizeof(struct check_data))) == NULL){
+		LM_ERR("couldn't allocate shared memory.\n");
+		shm_free(ret);
+		return NULL;
+	}
+	memset(ret->data, 0, sizeof(struct check_data));
+	
+	if(lock_init(&ret->data->flag_lock) == 0){
+		LM_ERR("cannot initialise flag lock.\n");
+		shm_free(ret->data);
+		shm_free(ret);
+		return NULL;
+	}
+	return ret;
+}
+
+static void destroy_element(struct check_list_element * element){
+	if(element){
+		if(element->data){
+/*		if(element->data->flag_lock){ */
+				lock_destroy(&element->data->flag_lock);
+/*			}*/
+			shm_free(element->data);
+		}
+		shm_free(element);
+	}
+	return;
+}

+ 61 - 0
modules_k/p_usrloc/ul_check.h

@@ -0,0 +1,61 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_CHECK_H
+#define SP_P_USRLOC_CHECK_H
+
+#include "../../lock_ops.h"
+
+struct check_data {
+	int refresh_flag;
+	int reconnect_flag;
+	gen_lock_t flag_lock;
+};
+
+struct check_list_element{
+	struct check_data * data;
+	struct check_list_element * next;
+};
+
+struct check_list_head{
+	gen_lock_t list_lock;
+	int element_count;
+	struct check_list_element * first;
+};
+
+int init_list(void);
+
+struct check_data * get_new_element(void);
+
+int must_refresh(struct check_data * element);
+
+int must_reconnect(struct check_data * element);
+
+int set_must_refresh(void);
+
+int set_must_reconnect(void);
+
+void destroy_list(void);
+
+int must_retry(time_t * timer, time_t interval);
+
+
+#endif

+ 440 - 0
modules_k/p_usrloc/ul_db.c

@@ -0,0 +1,440 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "../../lib/srdb1/db.h"
+#include "ul_db.h"
+#include "ul_mod.h"
+#include "ul_db_failover.h"
+#include "ul_db_ins.h"
+#include "ul_db_repl.h"
+#include "ul_db_ins_upd.h"
+#include "ul_db_upd.h"
+#include "ul_db_del.h"
+#include "ul_db_query.h"
+#include "ul_check.h"
+#include <unistd.h>
+
+ul_db_handle_t dbh_tmp;
+
+ul_master_db_set_t mdb;
+
+int required_caps = DB_CAP_QUERY | DB_CAP_RAW_QUERY | DB_CAP_INSERT | DB_CAP_DELETE | DB_CAP_UPDATE | DB_CAP_INSERT_UPDATE;
+
+static char query[UL_DB_QUERY_LEN];
+
+typedef struct db_dbf_dbres {
+	db1_res_t * res;
+	db_func_t * dbf;
+} db_dbf_dbres_t;
+
+#define UL_DB_RES_LIMIT 20
+
+db_dbf_dbres_t results[UL_DB_RES_LIMIT];
+
+static int add_dbf(db1_res_t * res, db_func_t * dbf);
+
+static db_func_t * get_and_remove_dbf(db1_res_t * res);
+
+int ul_db_init(void) {
+	mdb.read.url = &read_db_url;
+	mdb.write.url = &write_db_url;
+	
+	memset(results, 0, sizeof(results));
+
+	if(db_master_write){
+		if(db_bind_mod(mdb.write.url, &mdb.write.dbf) < 0) {
+			LM_ERR("could not bind api for write db.\n");
+			return -1;
+		}
+		if(!(mdb.write.dbf.cap & required_caps)) {
+			LM_ERR("db api of write db doesn't support required operation.\n");
+			return -1;
+		}
+		LM_INFO("write db initialized");
+	}
+	
+	if(db_bind_mod(mdb.read.url, &mdb.read.dbf) < 0) {
+		LM_ERR("could not bind db api for read db.\n");
+		return -1;
+	}
+	if(!(mdb.read.dbf.cap & required_caps)) {
+		LM_ERR("db api of read db doesn't support required operation.\n");
+		return -1;
+	}
+	LM_INFO("read db initialized");
+	return 0;
+}
+
+int ul_db_child_init(void) {
+	if(mdb.read.dbh){
+		mdb.read.dbf.close(mdb.read.dbh);
+		mdb.read.dbh = NULL;
+	}
+	if(mdb.write.dbh){
+		mdb.write.dbf.close(mdb.write.dbh);
+		mdb.write.dbh = NULL;
+	}
+	if((mdb.read.dbh  = mdb.read.dbf.init(mdb.read.url)) == NULL) {
+		LM_ERR("could not connect to sip master db (read).\n");
+		return -1;
+	}
+	LM_INFO("read db connection for children initialized");
+	
+	if(ul_db_child_locnr_init() == -1) return -1;
+	
+	LM_INFO("location number is %d\n", max_loc_nr);
+	if(db_master_write){
+		if((mdb.write.dbh  = mdb.write.dbf.init(mdb.write.url)) == NULL) {
+			LM_ERR("could not connect to sip master db (write).\n");
+			return -1;
+		}
+		LM_INFO("write db connection for children initialized");
+	}
+	return 0;
+}
+
+int ul_db_child_locnr_init(void) {
+	if(!mdb.read.dbh){
+		LM_ERR("Sip master DB connection(read) is down");
+		return -1;
+	}
+	if(load_location_number(&mdb.read.dbf, mdb.read.dbh, &max_loc_nr) != 0){
+		LM_ERR("could not load location number\n");
+		return -1;
+	}
+	return 0;
+}
+
+void ul_db_shutdown(void) {
+	destroy_handles();
+	if(mdb.read.dbh){
+		mdb.read.dbf.close(mdb.read.dbh);
+	}
+	if(mdb.write.dbh){
+		mdb.write.dbf.close(mdb.write.dbh);
+	}
+	return;
+}
+
+
+int db_handle_error(ul_db_handle_t * handle, int no) {
+	int query_len;
+	ul_db_t * db;
+	int i;
+	str tmp;
+	
+	if(!handle){
+		LM_ERR("NULL pointer in parameter.\n");
+		return -1;
+	}
+	
+	if(!db_master_write){
+		return 0;
+	}
+
+	query_len = 35 + reg_table.len
+			+ error_col.len * 2 + id_col.len;
+	
+	if(query_len > UL_DB_QUERY_LEN){
+		LM_ERR("query too long\n");
+		return -1;
+	}
+	
+	if((db = get_db_by_num(handle, no)) == NULL){
+		LM_ERR("can't get db.\n");
+		return -1;
+	}
+	
+	if (sprintf(query, "UPDATE %.*s "
+				   "SET %.*s=%.*s+1 "
+				   "WHERE %.*s=%i "
+				   "AND %.*s=%i",
+				   reg_table.len, reg_table.s,
+				   error_col.len, error_col.s, error_col.len, error_col.s, 
+				   id_col.len, id_col.s, handle->id,
+		   		   num_col.len, num_col.s, db->no) < 0) {
+		LM_ERR("could not print the query\n");
+		return -1;
+	}
+	tmp.s = query;
+	tmp.len = strlen(query);
+
+	if (mdb.write.dbf.raw_query (mdb.write.dbh, &tmp, NULL)) {
+		LM_ERR("error in database update.\n");
+		return -1;
+	}
+	
+	for(i=0; i<DB_NUM; i++){
+		if (handle->db[i].dbh && handle->db[i].dbf.close){
+			handle->db[i].dbf.close(handle->db[i].dbh);
+			handle->db[i].dbh = NULL;
+		}
+	}
+
+	if(load_data(&mdb.read.dbf, mdb.read.dbh, &dbh_tmp, handle->id) < 0){
+		LM_ERR("could not load id %i\n", handle->id);
+		return -1;
+	}
+	refresh_handle(handle, &dbh_tmp, 0);
+	LM_ERR("error on id %i, db %i, "
+		    "errors occured: %i, threshold: %i\n",
+		handle->id, db->no, db->errors, db_error_threshold);
+	if(db->errors >= db_error_threshold) {
+		LM_DBG("db_handle_error: now doing failover");
+		if((db_failover(&mdb.write.dbf, mdb.write.dbh, handle, no)) < 0) {
+			LM_ERR("error in doing failover.\n");
+			return -1;
+		}
+		if(load_data(&mdb.read.dbf, mdb.read.dbh, &dbh_tmp, handle->id) < 0){
+			return -1;
+		}
+		refresh_handle(handle, &dbh_tmp, 0);
+		set_must_refresh();
+	}
+	return 0;
+}
+
+int db_check_policy(int pol, int ok, int working) {
+#define DB_POL_N_1 0
+#define DB_POL_N_HALF 1
+#define DB_POL_N_ALL 2
+
+	switch(policy) {
+			case DB_POL_N_1:
+			switch(pol) {
+					case DB_POL_OP: if(ok >= (DB_NUM - 1)) {
+						return 0;
+					} else {
+						return -1;
+					}
+					break;
+					case DB_POL_QUERY: if(ok >= 1) {
+						return 0;
+					} else {
+						return -1;
+					}
+					case DB_POL_MOD: if((ok == working) && (working >= (DB_NUM - 1))) {
+						return 0;
+					} else {
+						return -1;
+					}
+					default: LM_ERR("wrong mode given.\n");
+						return -1;
+					}
+			case DB_POL_N_HALF:
+			switch(pol) {
+					case DB_POL_OP: if(ok >= (DB_NUM / 2)) {
+						return 0;
+					} else {
+						return -1;
+					}
+					break;
+					case DB_POL_QUERY: if(ok >= 1) {
+						return 0;
+					} else {
+						return -1;
+					}
+					case DB_POL_MOD: if((ok == working) && (working >= (DB_NUM / 2))) {
+						return 0;
+					} else {
+						return -1;
+					}
+					default: LM_ERR("wrong mode given.\n");
+						return -1;
+					}
+
+			case DB_POL_N_ALL:
+			switch(pol) {
+					case DB_POL_OP: if(ok == DB_NUM) {
+						return 0;
+					} else {
+						return -1;
+					}
+					break;
+					case DB_POL_QUERY: if(ok >= 1) {
+						return 0;
+					} else {
+						return -1;
+					}
+					case DB_POL_MOD: if(ok == DB_NUM) {
+						return 0;
+					} else {
+						return -1;
+					}
+					default: LM_ERR("wrong mode given.\n");
+					return -1;
+			}
+			default:
+				return -1;
+	}
+}
+
+int ul_db_insert(str * table, str * first, str * second,
+                 db_key_t* _k, db_val_t* _v, int _n) {
+	ul_db_handle_t * handle;
+	if(!db_write){
+		LM_ERR("not allowed in read only mode, abort.\n");
+		return -1;
+	}
+	if((handle = get_handle(&mdb.read.dbf, mdb.read.dbh, first, second))  == NULL) {
+		LM_ERR("could not retrieve db handle.\n");
+		return -1;
+	}
+	return db_insert(handle, table, _k, _v, _n);
+}
+
+int ul_db_replace(str * table, str * first, str * second,
+                 db_key_t* _k, db_val_t* _v, int _n) {
+	ul_db_handle_t * handle;
+	if(!db_write){
+		LM_ERR("not allowed in read only mode, abort.\n");
+		return -1;
+	}
+	if((handle = get_handle(&mdb.read.dbf, mdb.read.dbh, first, second))  == NULL) {
+		LM_ERR("could not retrieve db handle.\n");
+		return -1;
+	}
+	return db_replace(handle, table, _k, _v, _n);
+}
+
+int ul_db_update(str * table, str * first, str * second,
+				db_key_t* _k, db_op_t * _op, db_val_t* _v,
+				db_key_t* _uk, db_val_t* _uv, int _n, int _un) {
+	ul_db_handle_t * handle;
+	if(!db_write){
+		LM_ERR("not allowed in read only mode, abort.\n");
+		return -1;
+	}
+	if((handle = get_handle(&mdb.read.dbf, mdb.read.dbh, first, second))  == NULL) {
+		LM_ERR("could not retrieve db handle.\n");
+		return -1;
+	}
+	return db_update(handle, table, _k, _op, _v, _uk, _uv, _n, _un);
+}
+
+int ul_db_insert_update(str * table, str * first, str * second,
+                        db_key_t* _k, db_val_t* _v, int _n) {
+	ul_db_handle_t * handle;
+	if(!db_write){
+		LM_ERR("not allowed in read only mode, abort.\n");
+		return -1;
+	}
+	if((handle = get_handle(&mdb.read.dbf, mdb.read.dbh, first, second))  == NULL) {
+		LM_ERR("could not retrieve db handle.\n");
+		return -1;
+	}
+	return db_insert_update(handle, table, _k, _v, _n);
+}
+
+int ul_db_delete(str * table, str * first, str * second,
+                 db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n) {
+	ul_db_handle_t * handle;
+	if(!db_write){
+		LM_ERR("not allowed in read only mode, abort.\n");
+		return -1;
+	}
+	if((handle = get_handle(&mdb.read.dbf, mdb.read.dbh, first, second))  == NULL) {
+		LM_ERR("could not retrieve db handle.\n");
+		return -1;
+	}
+	return db_delete(handle, table, _k, _o, _v, _n);
+}
+
+int ul_db_query(str * table, str * first, str * second, db1_con_t *** _r_h, 
+				db_key_t* _k, db_op_t* _op,	db_val_t* _v,
+                db_key_t* _c, int _n, int _nc,	db_key_t _o, db1_res_t** _r) {
+	ul_db_handle_t * handle;
+	db_func_t * f;
+	int ret;
+	if((handle = get_handle(&mdb.read.dbf, mdb.read.dbh, first, second)) == NULL) {
+		LM_ERR("could not retrieve db handle.\n");
+		return -1;
+	}
+	if((ret = db_query(handle, _r_h, &f, table, _k, _op, _v, _c, _n, _nc, _o, _r, db_master_write)) < 0){
+		return ret;
+	}
+	add_dbf(*_r, f);
+	return ret;
+}
+
+int ul_db_free_result(db1_con_t ** dbh, db1_res_t * res){
+	db_func_t * f;
+	if(!dbh){
+		LM_ERR("NULL pointer in parameter.\n");
+		return -1;
+	}
+	if((f = get_and_remove_dbf(res)) == NULL){
+		return -1;
+	}
+	return f->free_result(*dbh, res);
+}
+
+int db_reactivate(ul_db_handle_t * handle, int no){
+	if(!db_master_write){
+		LM_ERR("running in read only mode, abort.\n");
+		return -1;
+	}
+	return db_failover_reactivate(&mdb.write.dbf, mdb.write.dbh, handle, no);
+}
+
+int db_reset_failover_time(ul_db_handle_t * handle, int no){
+	if(!db_master_write){
+		LM_ERR("running in read only mode, abort.\n");
+		return -1;
+	}
+	return db_failover_reset(&mdb.write.dbf, mdb.write.dbh, handle->id, no);
+}
+
+int ul_db_check(ul_db_handle_t * handle){
+	if(db_master_write){
+		return check_handle(&mdb.write.dbf, mdb.write.dbh, handle);
+	} else {
+		LM_ERR("checking is useless in read-only mode\n");
+		return 0;
+	}
+}
+
+static int add_dbf(db1_res_t * res, db_func_t * dbf){
+	int i=0;
+	for(i=0;i<UL_DB_RES_LIMIT;i++){
+		if(!results[i].res){
+			results[i].res = res;
+			results[i].dbf = dbf;
+			return 0;
+		}
+	}
+	LM_ERR("no free dbf tmp mem, maybe forgotten to cleanup result sets?\n");
+	return -1;
+}
+
+static db_func_t * get_and_remove_dbf(db1_res_t * res){
+	int i=0;
+	db_func_t * f;
+	for(i=0; i<UL_DB_RES_LIMIT; i++){
+		if(results[i].res == res){
+			f = results[i].dbf;
+			memset(&results[i], 0, sizeof(db_dbf_dbres_t));
+			return f;
+		}
+	}
+	LM_ERR("weird: dbf not found\n");
+	return NULL;
+}

+ 100 - 0
modules_k/p_usrloc/ul_db.h

@@ -0,0 +1,100 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_UL_DB_H
+#define SP_P_USRLOC_UL_DB_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+#define UL_DB_QUERY_LEN 2048
+
+#define DB_ERR_PRI 0
+#define DB_ERR_SEC 1
+#define DB_MODE_PRI 0
+#define DB_MODE_SEC 1
+#define DB_MODE_BOTH 2
+#define DB_MODE_OFF 3
+
+#define DB_POL_OP 0
+#define DB_POL_QUERY 1
+#define DB_POL_MOD 2
+
+#ifdef __i386__
+    #define UL_DB_ZERO_TIME 0x80000000
+#else
+    #define UL_DB_ZERO_TIME 0xFFFFFFFF80000000
+#endif
+
+typedef struct ul_master_db {
+	str * url;
+	db_func_t dbf;
+	db1_con_t * dbh;
+} ul_master_db_t;
+
+typedef struct ul_master_db_set {
+	ul_master_db_t read;
+	ul_master_db_t write;
+} ul_master_db_set_t;
+
+extern int required_caps;
+
+int ul_db_init();
+
+int ul_db_child_init();
+
+int ul_db_child_locnr_init();
+
+void ul_db_shutdown();
+
+int db_handle_error(ul_db_handle_t * handle, int no);
+
+int db_reactivate(ul_db_handle_t * handle, int no);
+
+int db_reset_failover_time(ul_db_handle_t * handle, int no);
+
+int db_check_policy(int pol, int ok, int working);
+
+int ul_db_insert(str * table, str * first, str * second,
+			  db_key_t* _k, db_val_t* _v, int _n);
+
+int ul_db_update(str * table, str * first, str * second,
+				db_key_t* _k, db_op_t * _op, db_val_t* _v,
+				db_key_t* _uk, db_val_t* _uv, int _n, int _un);
+
+int ul_db_insert_update(str * table, str * first, str * second,
+					 db_key_t* _k,	db_val_t* _v, int _n);
+
+int ul_db_replace(str * table, str * first, str * second,
+			  db_key_t* _k, db_val_t* _v, int _n);
+
+int ul_db_delete(str * table, str * first, str * second,
+			  db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
+
+int ul_db_query(str * table, str * first, str * second, db1_con_t *** _r_h,
+				db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c,
+				int _n, int _nc, db_key_t _o, db1_res_t** _r);
+
+int ul_db_free_result(db1_con_t ** dbh, db1_res_t * res);
+
+int ul_db_check(ul_db_handle_t * handle);
+
+#endif

+ 75 - 0
modules_k/p_usrloc/ul_db_api.c

@@ -0,0 +1,75 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_api.h"
+#include "../../sr_module.h"
+
+int bind_ul_db(ul_db_api_t* api)
+{
+	if (!api) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+	
+	api->insert = (ul_db_insert_t) ul_db_insert;
+	if(api->insert == 0){
+		LM_ERR("can't bind ul_db_insert\n");
+		return -1;
+	}
+
+	api->update = (ul_db_update_t) ul_db_update;
+	if(api->update == 0){
+		LM_ERR("can't bind ul_db_update\n");
+		return -1;
+	}
+	
+	api->insert_update = (ul_db_insert_update_t) ul_db_insert_update;
+	if(api->insert_update == 0){
+		LM_ERR("can't bind ul_db_insert_update\n");
+		return -1;
+	}
+	
+	api->replace = (ul_db_replace_t) ul_db_replace;
+	if(api->replace == 0){
+		LM_ERR("can't bind ul_db_replace\n");
+		return -1;
+	}
+	
+	api->delete = (ul_db_delete_t) ul_db_delete;
+	if(api->delete == 0){
+		LM_ERR("can't bind ul_db_delete\n");
+		return -1;
+	}
+	
+	api->query = (ul_db_query_t) ul_db_query;
+	if(api->query == 0){
+		LM_ERR("can't bind ul_db_query\n");
+		return -1;
+	}
+	
+	api->free_result = (ul_db_free_result_t) ul_db_free_result;
+	if(api->free_result == 0){
+		LM_ERR("can't bind ul_db_free_result\n");
+		return -1;
+	}
+
+	return 0;
+}

+ 78 - 0
modules_k/p_usrloc/ul_db_api.h

@@ -0,0 +1,78 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 UL_DB_API_H
+#define UL_DB_API_H
+
+#include "ul_db.h"
+#include "../../sr_module.h"
+
+typedef int (* ul_db_insert_t) (str * table, str * first, str * second,
+				 db_key_t* _k, db_val_t* _v, int _n);
+
+typedef int (* ul_db_update_t) (str * table, str * first, str * second,
+				db_key_t* _k, db_op_t * _op, db_val_t* _v, db_key_t* _uk,
+				db_val_t* _uv, int _n, int _un);
+
+typedef int (* ul_db_insert_update_t) (str * table, str * first, str * second,
+				db_key_t* _k, db_val_t* _v, int _n);
+
+typedef int (* ul_db_replace_t) (str * table, str * first, str * second,
+				db_key_t* _k, db_val_t* _v, int _n);
+
+typedef int (* ul_db_delete_t) (str * table, str * first, str * second,
+				 db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
+
+typedef int (* ul_db_query_t) (str * table, str * first, str * second, db1_con_t *** _r_h,
+				db_key_t* _k, db_op_t* _op,	db_val_t* _v, db_key_t* _c,
+				int _n, int _nc, db_key_t _o, db1_res_t** _r);
+
+typedef int (* ul_db_free_result_t)(db1_con_t ** dbh, db1_res_t * res);
+
+typedef struct ul_db_api{
+	ul_db_update_t			update;
+	ul_db_insert_t			insert;
+	ul_db_insert_update_t	insert_update;
+	ul_db_replace_t			replace;
+	ul_db_delete_t			delete;
+	ul_db_query_t			query;
+	ul_db_free_result_t		free_result;
+}ul_db_api_t;
+
+typedef int (*bind_ul_db_t)(ul_db_api_t * api);
+
+/*static inline int load_ul_db_api(ul_db_api_t * api) {
+	bind_ul_db_t ul_dbf;
+	
+	if(!(ul_dbf = (bind_ul_db_t)find_export("sp_ul_db_bind", 1, 0))) {
+		LM_ERR("can't import sp_ul_db_bind.\n");
+		return -1;
+	}
+	if(ul_dbf(api) < 0){
+		return -1;
+	}
+	return 0;
+}
+*/
+
+int bind_ul_db(ul_db_api_t* api);
+
+#endif

+ 29 - 0
modules_k/p_usrloc/ul_db_del.c

@@ -0,0 +1,29 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db.h"
+#include "ul_db_del.h"
+#include "ul_db_form_query.h"
+
+int db_delete(ul_db_handle_t * handle, str * table, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n) {
+	return db_submit_query(UL_DB_DEL, handle, table, _k, _o, _v, NULL, NULL, _n, 0);
+}
+

+ 30 - 0
modules_k/p_usrloc/ul_db_del.h

@@ -0,0 +1,30 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_DB_DEL_H
+#define SP_P_USRLOC_DB_DEL_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+int db_delete(ul_db_handle_t * handle, str * table, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
+
+#endif

+ 351 - 0
modules_k/p_usrloc/ul_db_failover.c

@@ -0,0 +1,351 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_failover.h"
+#include "ul_db_failover_func.h"
+#include "ul_db_handle.h"
+#include "ul_db.h"
+#include "ul_mod.h"
+
+static ul_db_handle_t spare;
+
+static ul_db_t tmp;
+
+static char query[UL_DB_QUERY_LEN];
+
+static int ul_db_failover_get_spare(db_func_t * dbf, db1_con_t * dbh, ul_db_t * db);
+
+static int ul_db_failover_switch(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no);
+
+static int ul_db_failover_normal(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no);
+
+int db_failover(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no) {
+	if(failover_level & FAILOVER_MODE_NORMAL){
+		if(ul_db_failover_normal(dbf, dbh, handle, no) < 0){
+			LM_ERR("could not switch to spare, try to "
+					"turn off broken db id %i, db %i.\n", 
+					handle->id, no);
+		} else {
+			return 0;
+		}
+	}
+	if(failover_level & (FAILOVER_MODE_NONE | FAILOVER_MODE_NORMAL)){
+		if(db_failover_deactivate(dbf, dbh, handle, no) < 0){
+			LM_ERR("could not deactivate "
+					"id %i, db %i.\n",
+				handle->id, no);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int ul_db_failover_normal(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no){
+	ul_db_t * db = NULL;
+	if(ul_db_failover_prepare(dbf, dbh) < 0){
+		LM_ERR("could not "
+				"initiate failover transaction, rollback.\n");
+		ul_db_failover_rollback(dbf, dbh);
+		return -1;
+	}
+	if((db = get_db_by_num(handle, no)) == NULL){
+		LM_ERR("could not find id %i, "
+				"db %i.\n", handle->id, no);
+		ul_db_failover_rollback(dbf, dbh);
+		return -1;
+	}
+	if(ul_db_failover_get_spare(dbf, dbh, db) < 0){
+		LM_ERR("no spare found. "
+				"id %i, db %i.\n", handle->id, no);
+		ul_db_failover_rollback(dbf, dbh);
+		return -1;
+	}
+	if(ul_db_failover_switch(dbf, dbh, handle, no) < 0){
+		LM_ERR("switch to spare on "
+				"id %i, db %i.\n", handle->id, no);
+		ul_db_failover_rollback(dbf, dbh);
+		return -1;
+	}
+	if(ul_db_failover_commit(dbf, dbh) < 0){
+		LM_ERR("could not "
+				"commit failover transaction, rollback.\n");
+		ul_db_failover_rollback(dbf, dbh);
+		return -1;
+	}
+	return 0;
+}
+
+static int ul_db_failover_get_spare(db_func_t * dbf, db1_con_t * dbh, ul_db_t * db) {
+	db1_res_t * res = NULL;
+	db_row_t * row;
+	int query_len;
+	str tmp;
+
+	if(!dbf || !dbh || !db) {
+		LM_ERR("Null pointer as parameter.\n");
+		return -1;
+	}
+
+	memset(&spare, 0, sizeof(ul_db_handle_t));
+	memset(query, 0, UL_DB_QUERY_LEN);
+	
+	query_len = 100 
+			+ id_col.len
+			+ num_col.len
+			+ url_col.len
+			+ 2 * risk_group_col.len
+			+ reg_table.len
+			+ spare_col.len
+			+ status_col.len;
+	
+	if(query_len >= UL_DB_QUERY_LEN){
+		LM_ERR("weird: extremely long query.\n");
+		return -1;
+	}
+	
+	if (sprintf(query,
+			"SELECT "
+			"%.*s, "
+			"%.*s, "
+			"%.*s, "
+			"%.*s "
+			"FROM %.*s "
+			"WHERE "
+			"%.*s=1 AND "
+			"%.*s!=%i AND "
+			"%.*s=%i "
+			"LIMIT 1 "
+			"FOR UPDATE",
+			id_col.len, id_col.s,
+			num_col.len, num_col.s,
+			url_col.len, url_col.s,
+			risk_group_col.len, risk_group_col.s,
+			reg_table.len, reg_table.s,
+			spare_col.len, spare_col.s,
+			risk_group_col.len, risk_group_col.s, db->rg,
+			status_col.len, status_col.s, DB_ON) < 0) {
+		LM_ERR("could not print query\n");
+		return -1;
+	}
+	tmp.s = query;
+	tmp.len = strlen(query);
+
+	if(dbf->raw_query(dbh, &tmp, &res) < 0) {
+		LM_ERR("could not query database.\n");
+		return -1;
+	}
+
+	if(RES_ROW_N(res) == 0) {
+		LM_ERR("no spare left.\n");
+		dbf->free_result(dbh, res);
+		return -1;
+	}
+	
+	row = RES_ROWS(res);
+	spare.id = VAL_INT(ROW_VALUES(row) + 0);
+	spare.db[0].no = VAL_INT(ROW_VALUES(row) + 1);
+	if(strlen(VAL_STRING(ROW_VALUES(row) + 2)) >= UL_DB_URL_LEN){
+		LM_ERR("weird: "
+				"db URL longer than %i.\n", UL_DB_URL_LEN);
+		dbf->free_result(dbh, res);
+		return -1;
+	}
+	strcpy(spare.db[0].url.s, VAL_STRING(ROW_VALUES(row) + 2));
+	spare.db[0].url.len = strlen(spare.db[0].url.s);
+	spare.db[0].rg = VAL_INT(ROW_VALUES(row) + 3);
+		
+	dbf->free_result(dbh, res);
+	return 0;
+}
+
+static int ul_db_failover_switch(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no) {
+	ul_db_t * db;
+	int check;
+	int old_num, new_num;
+	
+	if((db = get_db_by_num(handle, no)) == NULL){
+		LM_ERR("could not find id %i, "
+				"db %i.\n", handle->id, no);
+		return -1;
+	}
+	memset(&tmp, 0, sizeof(ul_db_handle_t));
+	memmove(&tmp, db, sizeof(ul_db_t));
+	memset(tmp.url.s, 0, UL_DB_URL_LEN);
+	memmove(tmp.url.s, db->url.s, UL_DB_URL_LEN);
+	
+	check = check_handle_data(dbf, dbh, db, handle->id);
+	if(check < 0){
+		LM_ERR("data check failed.\n");
+		return -1;
+	} else if(check > 0){
+		LM_ERR("failover already done.\n");
+		return 0;
+	}
+	if((new_num = get_max_no_of_db_id(dbf, dbh, spare.id)) < 0){
+		LM_ERR("getting highest num failed.\n");
+		return -1;
+	}
+	new_num++;
+	old_num = tmp.no;
+	tmp.no = new_num;
+	tmp.status = DB_OFF;
+	tmp.failover_time = time(NULL);
+	tmp.spare = 1;
+	if(store_handle_data(dbf, dbh, &tmp, handle->id, old_num, spare.id) < 0){
+		LM_ERR("storing data of broken db failed.\n");
+		return -1;
+	}
+	spare.db[0].failover_time = time(NULL);
+	spare.db[0].errors = 0;
+	old_num = spare.db[0].no;
+	spare.db[0].no = db->no;
+	if(store_handle_data(dbf, dbh, &spare.db[0], spare.id, old_num, handle->id) < 0){
+		LM_ERR("storing data of activated spare db failed.\n");
+		return -1;
+	}
+	return 0;
+}
+
+int db_failover_deactivate(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no) {
+	db_key_t cols[3];
+	db_key_t keys[3];
+	db_val_t vals[3];
+	db_val_t key_vals[3];
+	db_op_t op[2];
+
+	cols[0] = &status_col;
+	vals[0].type = DB1_INT;
+	vals[0].nul = 0;
+	vals[0].val.int_val = DB_OFF;
+	
+	cols[1] = &failover_time_col;
+	vals[1].type  = DB1_DATETIME;
+	vals[1].nul = 0;
+	vals[1].val.time_val = time(NULL);
+	
+	keys[0] = &id_col;
+	op[0] = OP_EQ;
+	key_vals[0].type = DB1_INT;
+	key_vals[0].nul = 0;
+	key_vals[0].val.int_val = handle->id;
+	
+	keys[1] = &num_col;
+	op[1] = OP_EQ;
+	key_vals[1].type = DB1_INT;
+	key_vals[1].nul = 0;
+	key_vals[1].val.int_val = no;
+
+	if(dbf->use_table(dbh, &reg_table) < 0) {
+		LM_ERR("could not use reg_table.\n");
+		return -1;
+	}
+
+	if(dbf->update(dbh, keys, op, key_vals, cols, vals, 2, 2) < 0) {
+		LM_ERR("could not update reg_table.\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+
+int db_failover_reactivate(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no) {
+	db_key_t cols[3];
+	db_key_t keys[2];
+	db_val_t vals[3];
+	db_val_t key_vals[2];
+	db_op_t op[2];
+
+	cols[0] = &status_col;
+	vals[0].type = DB1_INT;
+	vals[0].nul = 0;
+	vals[0].val.int_val = DB_ON;
+	
+	cols[1] = &failover_time_col;
+	vals[1].type  = DB1_DATETIME;
+	vals[1].nul = 0;
+	vals[1].val.time_val = time(NULL);
+	
+	cols[2] = &error_col;
+	vals[2].type  = DB1_INT;
+	vals[2].nul = 0;
+	vals[2].val.time_val = 0;
+	
+	keys[0] = &id_col;
+	op[0] = OP_EQ;
+	key_vals[0].type = DB1_INT;
+	key_vals[0].nul = 0;
+	key_vals[0].val.int_val = handle->id;
+	
+	keys[1] = &num_col;
+	op[1] = OP_EQ;
+	key_vals[1].type = DB1_INT;
+	key_vals[1].nul = 0;
+	key_vals[1].val.int_val = no;
+
+	if(dbf->use_table(dbh, &reg_table) < 0) {
+		LM_ERR("could not use reg_table.\n");
+		return -1;
+	}
+
+	if(dbf->update(dbh, keys, op, key_vals, cols, vals, 2, 3) < 0) {
+		LM_ERR("could not update reg_table.\n");
+		return -1;
+	}
+	return 0;
+}
+
+int db_failover_reset(db_func_t * dbf, db1_con_t * dbh,int id, int no){
+	db_key_t cols[1];
+	db_key_t keys[2];
+	db_val_t vals[1];
+	db_val_t key_vals[2];
+	db_op_t op[2];
+	
+	cols[0] = &failover_time_col;
+	vals[0].type  = DB1_DATETIME;
+	vals[0].nul = 0;
+	vals[0].val.time_val = UL_DB_ZERO_TIME;
+	
+	keys[0] = &id_col;
+	op[0] = OP_EQ;
+	key_vals[0].type = DB1_INT;
+	key_vals[0].nul = 0;
+	key_vals[0].val.int_val = id;
+	
+	keys[1] = &num_col;
+	op[1] = OP_EQ;
+	key_vals[1].type = DB1_INT;
+	key_vals[1].nul = 0;
+	key_vals[1].val.int_val = no;
+
+	
+
+	if(dbf->use_table(dbh, &reg_table) < 0) {
+		LM_ERR("could not use reg_table.\n");
+		return -1;
+	}
+	if(dbf->update(dbh, keys, op, key_vals, cols, vals, 2, 1) < 0) {
+		LM_ERR("could not update reg_table.\n");
+		return -1;
+	}
+	return 0;
+}

+ 41 - 0
modules_k/p_usrloc/ul_db_failover.h

@@ -0,0 +1,41 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_DB_FAILOVER_H
+#define SP_P_USRLOC_DB_FAILOVER_H
+
+#include "ul_db_handle.h"
+#include "../../lib/srdb1/db.h"
+
+#define FAILOVER_MODE_NONE 1
+#define FAILOVER_MODE_NORMAL (1<<1)
+#define FAILOVER_MODE_LAST (1<<2)
+
+int db_failover(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no);
+
+int db_failover_deactivate(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no);
+
+int db_failover_reactivate(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int no);
+
+int db_failover_reset(db_func_t * dbf, db1_con_t * dbh, int id, int no);
+
+
+#endif

+ 239 - 0
modules_k/p_usrloc/ul_db_failover_func.c

@@ -0,0 +1,239 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_failover_func.h"
+
+#include "../../dprint.h"
+#include "ul_mod.h"
+#include "ul_db.h"
+
+static str autocommit_off = str_init("SET AUTOCOMMIT=0");
+static str fail_isolation_level = str_init("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
+static str start_transaction = str_init("START TRANSACTION");
+static str commit = str_init("COMMIT");
+static str rollback = str_init("ROLLBACK");
+static str autocommit_on = str_init("SET AUTOCOMMIT=1");
+static char query[UL_DB_QUERY_LEN];
+
+int ul_db_failover_prepare(db_func_t * dbf, db1_con_t * dbh) {
+	if(dbf->raw_query(dbh, &autocommit_off, NULL) < 0) {
+		LM_ERR("could not "
+				"set autocommit off!\n");
+		return -2;
+	}
+	if(dbf->raw_query(dbh, &fail_isolation_level, NULL) < 0) {
+		LM_ERR("could not "
+				"set transaction isolation level!\n");
+		return -2;
+	}
+	if(dbf->raw_query(dbh, &start_transaction, NULL) < 0) {
+		LM_ERR("could not "
+				"start transaction!\n");
+		return -2;
+	}
+	return 0;
+}
+
+int ul_db_failover_commit(db_func_t * dbf, db1_con_t * dbh) {
+	if(dbf->raw_query(dbh, &commit, NULL) < 0) {
+		LM_ERR("transaction commit "
+				"failed.\n");
+		return -1;
+	}
+	if(dbf->raw_query(dbh, &autocommit_on, NULL) < 0) {
+		LM_ERR("could not turn "
+				"transaction autocommit on.\n");
+		return -2;
+	}
+	return 0;
+}
+
+int ul_db_failover_rollback(db_func_t * dbf, db1_con_t * dbh) {
+	LM_ERR("rolling back failover "
+			"transaction.\n");
+	if(dbf->raw_query(dbh, &rollback, NULL) < 0) {
+		LM_ERR("could not rollback "
+				"transaction.\n");
+		return -1;
+	}
+	if(dbf->raw_query(dbh, &autocommit_on, NULL) < 0) {
+		LM_ERR("could not set "
+				"autocommit on.\n");
+		return -2;
+	}
+	return 0;
+}
+
+int get_max_no_of_db_id(db_func_t * dbf, db1_con_t * dbh, int id){
+	db1_res_t * res;
+	db_row_t * row;
+	int query_len, max;
+	str tmp;
+
+	query_len = 50 + reg_table.len + id_col.len + num_col.len;
+	if(query_len > UL_DB_QUERY_LEN){
+		LM_ERR("weird: query too long.\n");
+		return -1;
+	}
+	memset(query, 0, UL_DB_QUERY_LEN);
+	if (sprintf(query,
+			"SELECT MAX(%.*s) "
+			"FROM %.*s "
+			"WHERE %.*s='%i'",
+			num_col.len, num_col.s,
+			reg_table.len, reg_table.s,
+			id_col.len, id_col.s, id) < 0) {
+		LM_ERR("could not print query\n");
+		return -1;
+	}
+	tmp.s = query;
+	tmp.len = strlen(query);
+	
+	if(dbf->raw_query(dbh, &tmp, &res) < 0){
+		LM_ERR("weird: could not query %.*s.\n",
+		   reg_table.len, reg_table.s);
+		return -1;
+	}
+	if(RES_ROW_N(res) == 0){
+		LM_ERR("weird: no data found for id %i\n", id);
+		dbf->free_result(dbh, res);
+		return -1;
+	}
+	row = RES_ROWS(res);
+	max = VAL_INT(ROW_VALUES(row));
+	dbf->free_result(dbh, res);
+	return max;
+}
+
+int store_handle_data(db_func_t * dbf, db1_con_t * dbh, ul_db_t * db, int id, int old_num, int new_id){
+	db_key_t cols[8];
+	db_val_t vals[8];
+	db_key_t keys[2];
+	db_val_t key_vals[8];
+	db_op_t op[2];
+	
+	cols[0] = &id_col;
+	vals[0].type = DB1_INT;
+	vals[0].nul = 0;
+	vals[0].val.int_val = new_id;
+	
+	cols[1] = &num_col;
+	vals[1].type = DB1_INT;
+	vals[1].nul = 0;
+	vals[1].val.int_val = db->no;
+	
+	cols[2] = &url_col;
+	vals[2].type = DB1_STRING;
+	vals[2].nul = 0;
+	vals[2].val.string_val = db->url.s;
+	
+	cols[3] = &error_col;
+	vals[3].type = DB1_INT;
+	vals[3].nul = 0;
+	vals[3].val.int_val = db->errors;
+	
+	cols[4] = &failover_time_col;
+	vals[4].type = DB1_DATETIME;
+	vals[4].nul = 0;
+	vals[4].val.time_val = db->failover_time;
+	
+	cols[5] = &spare_col;
+	vals[5].type = DB1_INT;
+	vals[5].nul = 0;
+	vals[5].val.int_val = db->spare;
+	
+	cols[6] = &status_col;
+	vals[6].type = DB1_INT;
+	vals[6].nul = 0;
+	vals[6].val.int_val = db->status;
+	
+	cols[7] = &risk_group_col;
+	vals[7].type = DB1_INT;
+	vals[7].nul = 0;
+	vals[7].val.int_val = db->rg;
+	
+	keys[0] = &id_col;
+	op[0] = OP_EQ;
+	key_vals[0].type = DB1_INT;
+	key_vals[0].nul = 0;
+	key_vals[0].val.int_val = id;
+	
+	keys[1] = &num_col;
+	op[1] = OP_EQ;
+	key_vals[1].type = DB1_INT;
+	key_vals[1].nul = 0;
+	key_vals[1].val.int_val = old_num;
+	
+	if(dbf->use_table(dbh, &reg_table) < 0){
+		LM_ERR("could not use reg_table.\n");
+		return -1;
+	}
+	if(dbf->update(dbh, keys, op, key_vals, cols, vals, 2, 7) < 0){
+		LM_ERR("could insert handle data.\n");
+		return -1;
+	}
+	return 0;
+}
+
+int check_handle_data(db_func_t * dbf, db1_con_t * dbh, ul_db_t * db, int id){
+	db_key_t cols[2];
+	db_key_t keys[4];
+	db_val_t key_vals[4];
+	db_op_t op[4];
+	db1_res_t * res;
+	
+	cols[0] = &id_col;
+	
+	keys[0] = &id_col;
+	op[0] = OP_EQ;
+	key_vals[0].type = DB1_INT;
+	key_vals[0].nul = 0;
+	key_vals[0].val.int_val = id;
+	
+	keys[1] = &num_col;
+	op[1] = OP_EQ;
+	key_vals[1].type = DB1_INT;
+	key_vals[1].nul = 0;
+	key_vals[1].val.int_val = db->no;
+	
+	keys[2] = &url_col;
+	op[2] = OP_EQ;
+	key_vals[2].type = DB1_STRING;
+	key_vals[2].nul = 0;
+	key_vals[2].val.string_val = db->url.s;
+	
+	if(dbf->use_table(dbh, &reg_table) < 0){
+		LM_ERR("could not use "
+				"reg table.\n");
+		return -1;
+	}
+	if(dbf->query(dbh, keys, op, key_vals, cols, 3, 1, NULL, &res) < 0){
+		LM_ERR("could not use "
+				"query table.\n");
+		return -1;
+	}
+	if(RES_ROW_N(res) == 0){
+		dbf->free_result(dbh, res);
+		return 1;
+	}
+	dbf->free_result(dbh, res);
+	return 0;
+}

+ 40 - 0
modules_k/p_usrloc/ul_db_failover_func.h

@@ -0,0 +1,40 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_UL_DB_FAILOVER_FUNC_H
+#define SP_P_USRLOC_UL_DB_FAILOVER_FUNC_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+int ul_db_failover_prepare(db_func_t * dbf, db1_con_t * dbh);
+
+int ul_db_failover_commit(db_func_t * dbf, db1_con_t * dbh);
+
+int ul_db_failover_rollback(db_func_t * dbf, db1_con_t * dbh);
+
+int get_max_no_of_db_id(db_func_t * dbf, db1_con_t * dbh, int id);
+
+int store_handle_data(db_func_t * dbf, db1_con_t * dbh, ul_db_t * db, int id, int old_num, int new_id);
+
+int check_handle_data(db_func_t * dbf, db1_con_t * dbh, ul_db_t * db, int id);
+
+#endif

+ 147 - 0
modules_k/p_usrloc/ul_db_form_query.c

@@ -0,0 +1,147 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_form_query.h"
+#include "ul_db_tran.h"
+#include "ul_db.h"
+#include "ul_mod.h"
+
+static int db_do_query(ul_db_op_t ul_op, db_func_t * dbf, db1_con_t * dbh, str * table, db_key_t* _k, db_op_t* _o,
+                       db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un);
+
+int db_submit_query(ul_db_op_t ul_op, ul_db_handle_t * handle, str * table, db_key_t* _k, db_op_t* _o,
+                    db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un) {
+	int i;
+	int working_c[DB_NUM];
+	int working_r[DB_NUM];
+	int errors = 0;
+	int w;
+
+	if(!handle || !table || !table->s) {
+		LM_ERR("NULL pointer in parameter.\n");
+		return -1;
+	}
+
+	if(db_use_transactions) {
+		for(i=0; i<DB_NUM; i++) {
+			working_c[i] = 0;
+			working_r[i] = 0;
+		}
+
+		if(ul_db_tran_start(handle, working_r) < 0) {
+			LM_ERR("error during starting transaction"
+			    " on table %.*s with id %i.\n", table->len, table->s, handle->id);
+			w = get_working_sum(working_r, DB_NUM);
+			if(db_check_policy(DB_POL_MOD, w, handle->working) < 0) {
+				ul_db_tran_rollback(handle, working_r);
+				return -1;
+			}
+		}
+
+		for(i=0; i<DB_NUM; i++) {
+			working_c[i] = working_r[i];
+			if((handle->db[i].status == DB_ON) && (working_c[i])) {
+				if(db_do_query(ul_op, &handle->db[i].dbf, handle->db[i].dbh, table, _k, _o, _v, _uk, _uv, _n, _un) < 0) {
+					LM_ERR("error during querying "
+					    "table %.*s with id %i on db %i.\n",
+					    table->len, table->s, handle->id, i);
+					if(db_handle_error(handle, handle->db[i].no) < 0) {
+						LM_CRIT("could not handle error on db %i, handle, %i\n",
+						    handle->id, handle->db[i].no);
+					}
+					errors++;
+					working_c[i] = 0;
+				} else {
+					working_r[i] = 0;
+				}
+			}
+		}
+
+		w = get_working_sum(working_c, DB_NUM);
+		if(errors > 0) {
+			ul_db_tran_rollback(handle, working_r);
+			if(db_check_policy(DB_POL_MOD, w, handle->working) < 0) {
+				ul_db_tran_rollback(handle, working_c);
+				return -1;
+			}
+		}
+		return ul_db_tran_commit(handle, working_c);
+	} else {
+		for(i=0; i<DB_NUM; i++) {
+			if(handle->db[i].status == DB_ON) {
+				if(db_do_query(ul_op, &handle->db[i].dbf, handle->db[i].dbh, table, _k, _o, _v, _uk, _uv, _n, _un) < 0) {
+					if(db_handle_error(handle, handle->db[i].no) < 0) {
+						LM_CRIT("could not handle error on db %i, handle, %i\n",
+						    handle->id, handle->db[i].no);
+					}
+					return -1;
+				}
+			}
+		}
+		return 0;
+	}
+}
+
+static int db_do_query(ul_db_op_t ul_op, db_func_t * dbf, db1_con_t * dbh, str * table, db_key_t* _k, db_op_t* _o,
+                       db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un) {
+	if(dbf->use_table(dbh, table) < 0) {
+		LM_ERR("error in use table %.*s.\n", table->len, table->s);
+		return -1;
+	}
+	switch(ul_op) {
+			case UL_DB_INS:
+			if(dbf->insert(dbh, _k, _v, _n) < 0) {
+				LM_ERR("error in inserting into "
+				    "table %.*s.\n", table->len, table->s);
+				return -1;
+			}
+			return 0;
+			case UL_DB_REPL:
+			if(dbf->replace(dbh, _k, _v, _n) < 0) {
+				LM_ERR("error in replacing in "
+				    "table %.*s.\n", table->len, table->s);
+				return -1;
+			}
+			return 0;
+			case UL_DB_INS_UPD:
+			if(dbf->insert_update(dbh, _k, _v, _n) < 0) {
+				LM_ERR("error in inserting/updating "
+				    "table %.*s.\n", table->len, table->s);
+				return -1;
+			}
+			return 0;
+			case UL_DB_UPD:
+			if(dbf->update(dbh, _k, _o, _v, _uk, _uv, _n, _un) < 0) {
+				LM_ERR("error in updating "
+				    "table %.*s.\n", table->len, table->s);
+				return -1;
+			}
+			return 0;
+			case UL_DB_DEL:
+			if(dbf->delete(dbh, _k, _o, _v, _n) < 0) {
+				LM_ERR("error in deleting from table %.*s.\n", table->len, table->s);
+				return -1;
+			}
+			return 0;
+			default: return -1;
+	}
+	return 0;
+}

+ 48 - 0
modules_k/p_usrloc/ul_db_form_query.h

@@ -0,0 +1,48 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_UL_DB_FORM_QUERY_H
+#define SP_P_USRLOC_UL_DB_FORM_QUERY_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+typedef enum {
+	UL_DB_INS,
+	UL_DB_REPL,
+	UL_DB_INS_UPD,
+	UL_DB_UPD,
+	UL_DB_DEL,
+}ul_db_op_t;
+
+int db_submit_query(ul_db_op_t ul_op,
+					ul_db_handle_t * handle, 
+					str * table, 
+					db_key_t* _k, 
+					db_op_t* _o,
+					db_val_t* _v, 
+					db_key_t* _uk, 
+					db_val_t* _uv, 
+					int _n, 
+					int _un
+				   );
+
+#endif

+ 639 - 0
modules_k/p_usrloc/ul_db_handle.c

@@ -0,0 +1,639 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_handle.h"
+#include "ul_mod.h"
+#include "ul_db.h"
+#include "ul_db_watch.h"
+#include "../../lib/kcore/km_crc.h"
+
+static ul_db_handle_list_t * db_handles = NULL;
+
+static ul_db_handle_t tmp;
+
+static ul_db_handle_t * allocate_handle(void);
+
+static ul_db_handle_list_t * allocate_handle_list(void);
+
+static void free_handle(ul_db_handle_list_t * element);
+
+static int check_status(ul_db_handle_t * a, ul_db_handle_t * b);
+
+static int compute_id(str* first, str* second);
+static int load_id(db_func_t * dbf, db1_con_t * dbh, str * first, str * second);
+
+static int release_handle(ul_db_handle_t * handle);
+
+static int activate_handle(ul_db_handle_t * handle);
+
+ul_db_handle_t * get_handle(db_func_t * dbf, db1_con_t * dbh, str * first, str * second) {
+	struct ul_db_handle_list * element;
+	ul_db_handle_t * ret = NULL;
+	int db_ok = 0;
+	int id;
+
+	switch(alg_location){
+	case 0:{ 
+		if((id = load_id(dbf, dbh, first, second)) < 0){
+			return NULL;
+		}
+		 break;
+	}
+	case 1:{
+		if( max_loc_nr == 0){
+			LM_WARN("max_loc_nr is 0 . Try to recompute value\n");
+			if( load_location_number(dbf, dbh, &max_loc_nr) != 0 ){
+				LM_ERR("Could not get location number\n");
+				return NULL;
+			}
+		} 
+		if((id = compute_id(first, second)) < 0){
+			return NULL;
+		}
+		break;
+	}
+	default:{
+		LM_ERR("No suitable selection for location\n");
+		return NULL;
+	}
+	}
+	
+	if(load_data(dbf, dbh, &tmp, id) < 0){
+		// load_data should explain this
+		return NULL;
+	}
+
+	element  = db_handles;
+	db_ok = 0;
+	while(element && element->handle) {
+		if(element->handle->id == tmp.id) {
+			LM_DBG("found handle with id %i\n", element->handle->id);
+			element->handle->expires = time(NULL) + connection_expires;
+			if(check_status(element->handle, &tmp) == 0) {
+				db_ok = 1;
+			}
+			ret = element->handle;
+		}
+		if((element->handle->expires < time(NULL)) && element->handle->active){
+			release_handle(element->handle);
+		}
+		element = element->next;
+	}
+	if(db_ok) {
+		goto ret;
+	}
+
+	element = NULL;
+
+	if(ret == NULL) {
+		LM_DBG("didn't find handle with id %i\n", tmp.id);
+		if((element = allocate_handle_list()) == NULL) {
+			LM_ERR("could not allocate handle.\n");
+			return NULL;
+		}
+		ret = element->handle;
+		ret->id = tmp.id;
+		activate_handle(ret);
+		element->next = db_handles;
+		db_handles = element;
+	}
+	if(refresh_handle(ret, &tmp, db_write) < 0) {
+		ret = NULL;
+	}
+ret:
+	if(ret && !ret->active){
+		activate_handle(ret);
+	}
+	return ret;
+}
+
+int refresh_handle(ul_db_handle_t * handle, ul_db_handle_t * new_data, int error_handling) {
+	int db_ok = 0;
+	int i, ret;
+	str tmpurl;
+	handle->id = new_data->id;
+	
+	handle->working = 0;
+
+	handle->expires = time(NULL) + connection_expires;
+
+	for(i=0; i<DB_NUM; i++) {
+		handle->db[i].status = new_data->db[i].status;
+		handle->db[i].errors = new_data->db[i].errors;
+		handle->db[i].failover_time = new_data->db[i].failover_time;
+		handle->db[i].rg = new_data->db[i].rg;
+		handle->db[i].no = new_data->db[i].no;
+		
+		if((handle->db[i].url.len != new_data->db[i].url.len) || (strcmp(handle->db[i].url.s, new_data->db[i].url.s) != 0)) {
+			memset(handle->db[i].url.s, 0, UL_DB_URL_LEN);
+			strcpy(handle->db[i].url.s, new_data->db[i].url.s);
+			handle->db[i].url.len = new_data->db[i].url.len;
+			if(handle->db[i].dbh) {
+				handle->db[i].dbf.close(handle->db[i].dbh);
+				handle->db[i].dbh = NULL;
+			}
+			memset(&handle->db[i].dbf, 0, sizeof(db_func_t));
+			tmpurl.len = handle->db[i].url.len;
+			tmpurl.s = handle->db[i].url.s;
+			if(db_bind_mod(&tmpurl, &handle->db[i].dbf) < 0){
+				LM_ERR("could not bind db module.\n");
+				return -1;
+			}
+		}
+		if(handle->db[i].status == DB_ON) {
+			handle->working++;
+			if(handle->db[i].dbh) {
+				db_ok++;
+			} else {
+				LM_DBG("connect id %i db %i.\n", handle->id, handle->db[i].no);
+				tmpurl.len = handle->db[i].url.len;
+				tmpurl.s = handle->db[i].url.s;
+				if((handle->db[i].dbh = handle->db[i].dbf.init(&tmpurl)) == NULL) {
+					LM_ERR("id: %i could not "
+					    "connect database %i.\n", handle->id, handle->db[i].no);
+					if(error_handling){
+						if(db_handle_error(handle, handle->db[i].no) < 0){
+							LM_ERR("id: %i could not "
+								"handle error on database %i.\n", handle->id, handle->db[i].no);
+						}
+					}
+				} else {
+					db_ok++;
+				}
+			}
+		} else if (handle->db[i].status == DB_INACTIVE) {
+			if(handle->db[i].dbh){
+				LM_DBG("deactivate id %i db %i.\n", handle->id, handle->db[i].no);
+				handle->db[i].dbf.close(handle->db[i].dbh);
+				handle->db[i].dbh = NULL;
+			}
+		} else {
+			if(handle->db[i].dbh) {
+				LM_DBG("shutdown id %i db %i.\n", handle->id, handle->db[i].no);
+				handle->db[i].dbf.close(handle->db[i].dbh);
+				handle->db[i].dbh = NULL;
+			}
+		}
+	}
+	if((ret = db_check_policy(DB_POL_OP, db_ok, handle->working)) < 0){
+		LM_ERR("id %i: too few dbs working\n", handle->id);
+	}
+	return ret;
+}
+
+static int release_handle(ul_db_handle_t * handle){
+	int i;
+	LM_NOTICE("deactivating handle id %i, db 1:  %.*s, db2:  %.*s\n", handle->id, handle->db[0].url.len, handle->db[0].url.s, handle->db[1].url.len, handle->db[1].url.s);
+	for(i=0; i<DB_NUM; i++){
+		if(handle->db[i].dbh){
+			handle->db[i].dbf.close(handle->db[i].dbh);
+			handle->db[i].dbh = NULL;
+		}
+	}
+	handle->active = 0;
+	return ul_unregister_watch_db(handle->id);
+}
+
+static int activate_handle(ul_db_handle_t * handle){
+	LM_NOTICE("activating handle id %i, db 1: %.*s, db2: %.*s\n", handle->id, handle->db[0].url.len, handle->db[0].url.s, handle->db[1].url.len, handle->db[1].url.s);
+	handle->active = 1;
+	return ul_register_watch_db(handle->id);
+}
+
+static ul_db_handle_list_t * allocate_handle_list(void) {
+	ul_db_handle_list_t * ret;
+	if((ret = (ul_db_handle_list_t *)pkg_malloc(sizeof(ul_db_handle_list_t))) == NULL) {
+		LM_ERR("couldn't allocate private memory.\n");
+		return NULL;
+	}
+	if((ret->handle = allocate_handle()) == NULL) {
+		pkg_free(ret);
+		return NULL;
+	}
+	return ret;
+}
+
+static ul_db_handle_t * allocate_handle(void) {
+	ul_db_handle_t * ret;
+	if((ret = (ul_db_handle_t *)pkg_malloc(sizeof(ul_db_handle_t))) == NULL) {
+		LM_ERR("couldn't allocate pkg mem.\n");
+		return NULL;
+	}
+	memset(ret, 0, sizeof(ul_db_handle_t));
+	if((ret->check = get_new_element()) == NULL) {
+		pkg_free(ret);
+		return NULL;
+	}
+	return ret;
+}
+int load_location_number(db_func_t * dbf, db1_con_t* dbh, int *loc_nr){
+	static char query[UL_DB_QUERY_LEN];
+	db1_res_t * res;
+	db_row_t * row;
+	int query_len;
+	str tmp;
+
+	if(!loc_nr || !dbf || !dbh){
+		LM_ERR("NULL parameter passed \n");
+		return -1;
+	}
+
+	query_len = 30 + id_col.len + reg_table.len + status_col.len;
+	if(query_len > UL_DB_QUERY_LEN) {
+		LM_ERR("weird: query larger than %i bytes.\n", UL_DB_QUERY_LEN);
+		return -1;
+	}
+	
+	memset(query, 0, UL_DB_QUERY_LEN);
+	
+	if(sprintf(query,
+			"SELECT MAX(%.*s) "
+			"FROM "
+			"%.*s "
+			"WHERE %.*s = 1;", id_col.len, id_col.s, reg_table.len, reg_table.s, status_col.len, status_col.s) < 0){
+			LM_ERR("could not sprinf query\n");
+			return -1;
+	}
+	LM_DBG("%s\n",query);
+	
+	tmp.s = query;
+	tmp.len = strlen(query);
+
+	if (dbf->raw_query (dbh, &tmp, &res) < 0) {
+		LM_ERR("in database query.\n");
+		return -1;
+	}
+
+	if (RES_ROW_N (res) == 0) {
+		dbf->free_result (dbh, res);
+		LM_DBG ("no data found\n");
+		return 1;
+	}
+
+	row = RES_ROWS(res) + 0; /* only one row in answer */
+	
+	if (VAL_NULL (ROW_VALUES(row) + 0)) {
+		LM_ERR("Weird: Empty Max ID Number\n");
+		dbf->free_result (dbh, res);
+		return 1;
+	}
+
+	*loc_nr = VAL_INT (ROW_VALUES(row) + 0);
+	dbf->free_result (dbh, res);
+	if(*loc_nr == 0){
+		LM_ERR("No location in DB?!\n");
+		return 1;
+	}
+	return 0;
+}
+
+int load_handles(db_func_t * dbf, db1_con_t * dbh) {
+	static char query[UL_DB_QUERY_LEN];
+	db1_res_t * res;
+	db_row_t * row;
+	ul_db_handle_list_t * element;
+	int i, id, query_len;
+	str tmp;
+
+	if(!dbf || !dbh){
+		LM_ERR("NULL parameter passed \n");
+		return -1;
+	}
+
+	query_len = 25 + id_col.len + reg_table.len;
+
+	if(query_len > UL_DB_QUERY_LEN) {
+		LM_ERR("weird: query larger than %i bytes.\n", UL_DB_QUERY_LEN);
+		return -1;
+	}
+
+	memset(query, 0, UL_DB_QUERY_LEN);
+
+	if (sprintf(query,
+	        "SELECT DISTINCT "
+	        "%.*s "
+	        "FROM "
+	        "%.*s",
+	        id_col.len, id_col.s,
+	        reg_table.len, reg_table.s) < 0) {
+		LM_ERR("could not print query\n");
+		return -1;
+	}
+	tmp.s = query;
+	tmp.len = strlen(query);
+
+	if (dbf->raw_query (dbh, &tmp, &res) < 0) {
+		LM_ERR("in database query.\n");
+		return -1;
+	}
+
+	if (RES_ROW_N (res) == 0) {
+		dbf->free_result (dbh, res);
+		LM_DBG ("no data found\n");
+		return 1;
+	}
+
+	for (i = 0; i < RES_ROW_N (res); ++i) {
+		row = RES_ROWS (res) + i;
+
+		if((element = allocate_handle_list()) == NULL) {
+			LM_ERR("couldnt allocate handle.\n");
+			goto errout;
+		}
+
+		if (VAL_NULL (ROW_VALUES(row) + 0)) {
+			LM_ERR("Weird: Empty ID-Field\n");
+			goto errout;
+		}
+
+		id = VAL_INT (ROW_VALUES(row) + 0);
+		if(load_data(dbf, dbh, element->handle, id) < 0){
+			LM_ERR("couldn't load handle data.\n");
+			goto errout;
+		}
+		element->next = db_handles;
+		db_handles = element;
+	}
+	dbf->free_result (dbh, res);
+	return 0;
+errout:
+	dbf->free_result (dbh, res);
+	return -1;
+}
+
+int refresh_handles(db_func_t * dbf, db1_con_t * dbh) {
+	ul_db_handle_list_t * element;
+	int i;
+	element = db_handles;
+	while(element) {
+		for(i=0; i<DB_NUM; i++) {
+			if(element->handle->db[i].dbh) {
+				dbf->close(element->handle->db[i].dbh);
+				element->handle->db[i].dbh = NULL;
+			}
+		}
+		if(load_data(dbf, dbh, &tmp, element->handle->id) < 0){
+			LM_ERR("couldn't load handle data.\n");
+			return -1;
+		}
+		if(refresh_handle(element->handle, &tmp, db_write) < 0) {
+			LM_ERR("couldn't refresh handle data.\n");
+			return -1;
+		}
+		element = element->next;
+	}
+	return 1;
+}
+
+void destroy_handles(void){
+	ul_db_handle_list_t * element, * del;
+	int i;
+	element = db_handles;
+	while(element){
+		for(i=0; i<DB_NUM; i++){
+			if(element->handle->db[i].dbh){
+				element->handle->db[i].dbf.close(element->handle->db[i].dbh);
+				element->handle->db[i].dbh = NULL;
+			}
+		}
+		del = element;
+		element = element->next;
+		free_handle(del);
+	}
+}
+
+ul_db_t * get_db_by_num(ul_db_handle_t * handle, int no) {
+	int i;
+	for(i=0; i<DB_NUM; i++) {
+		if(handle->db[i].no == no) {
+			return &handle->db[i];
+		}
+	}
+	return NULL;
+}
+
+int check_handle(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle){
+	int i;
+	str tmpurl;
+	LM_INFO("checking id %i\n", handle->id);
+	if(load_data(dbf, dbh, &tmp, handle->id) < 0){
+		return -1;
+	}
+	refresh_handle(handle, &tmp, 1);
+	for(i=0; i<DB_NUM; i++) {
+		if(handle->db[i].url.len > 0){
+			LM_INFO("checking id %i no %i, url %.*s, status %s\n", handle->id, handle->db[i].no, 
+				handle->db[i].url.len, handle->db[i].url.s, (handle->db[i].status == DB_ON ? "ON" : (handle->db[i].status == DB_OFF ? "OFF" : "DEACTIVATED")));
+			if(handle->db[i].status == DB_OFF) {
+				tmpurl.len = handle->db[i].url.len;
+				tmpurl.s = handle->db[i].url.s;
+				if((handle->db[i].dbh = handle->db[i].dbf.init(&tmpurl)) != NULL){
+					if(db_reactivate(handle, handle->db[i].no) < 0) {
+						LM_ERR("could not reactivate id %i, db %i.\n",
+							handle->id, handle->db[i].no);
+						handle->db[i].dbf.close(handle->db[i].dbh);
+						handle->db[i].dbh = NULL;
+					} else {
+						handle->db[i].status = DB_ON;
+						set_must_reconnect();
+					}
+				} else {
+					LM_NOTICE("%s: db id %i, no %i url %.*s is still down\n", __FUNCTION__, 
+						handle->id, handle->db[i].no, handle->db[i].url.len, handle->db[i].url.s);
+				}
+			} else if((handle->db[i].status == DB_ON) && handle->db[i].dbh) {
+				if((handle->db[i].failover_time < (time(NULL) - expire_time)) && (handle->db[i].failover_time != UL_DB_ZERO_TIME)){
+					LM_ERR("%s: failover_time: %ld, now: %ld, delta: %ld, now going to reset failover time\n", __FUNCTION__, 
+						handle->db[i].failover_time, time(NULL), (time(NULL) - handle->db[i].failover_time));
+					if(db_reset_failover_time(handle, handle->db[i].no) < 0) {
+						LM_ERR("could not reset failover time for id %i, db %i.\n",
+							handle->id, handle->db[i].no);
+					}
+				}
+			}
+		} else {
+			LM_ERR("id %i, no url to check\n", handle->id);
+		}
+	}	
+	return 1;
+}
+
+static void free_handle(ul_db_handle_list_t * element) {
+	if(element){
+		if(element->handle){
+			pkg_free(element->handle);
+		}
+		pkg_free(element);
+	}
+	return;
+}
+
+static int check_status(ul_db_handle_t * a, ul_db_handle_t * b){
+	int i;
+	if(!a->active){
+		LM_NOTICE("id %i is inactive\n", a->id);
+		return -1;
+	}
+	if(must_refresh(a->check)){
+		LM_NOTICE("id %i must be refreshed\n", a->id);
+		return -1;
+	}
+	if(must_reconnect(a->check)){
+		LM_NOTICE("id %i must be reconnected\n", a->id);
+		return -1;
+	}
+	for(i=0; i<DB_NUM; i++){
+		if(strcmp(a->db[i].url.s, b->db[i].url.s) != 0){
+			LM_NOTICE("id %i, db %s has different url\n", a->id, a->db[i].url.s);
+			return -1;
+		}
+		if(a->db[i].status != b->db[i].status){
+			LM_NOTICE("id %i, db %s has different status\n", a->id, a->db[i].url.s);
+			return -1;
+		}
+		if(a->db[i].no != b->db[i].no){
+			LM_NOTICE("id %i, db %s has different no\n", a->id, a->db[i].url.s);
+			return -1;
+		}
+		if((a->db[i].status == DB_ON) && (!a->db[i].dbh)){
+			LM_NOTICE("id %i, db %s has inconsistent status (1)\n", a->id, a->db[i].url.s);
+			return -1;
+		}
+		if((a->db[i].status == DB_OFF) && (a->db[i].dbh)){	
+			LM_NOTICE("id %i, db %s has inconsistent status (2)\n", a->id, a->db[i].url.s);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int compute_id(str* first, str* second){
+	unsigned int crc32_val;
+#define BUF_MAXSIZE  1024
+	char aux[BUF_MAXSIZE];
+	str tmp;
+	if(!first){
+		LM_ERR("Null first parameter received\n");
+		return -1;
+	}
+	
+	if(use_second_key){
+		//compute crc32(user@domain)
+		LM_DBG("XDBGX: compute_id HAS second key : %.*s", first->len, first->s);
+		if(!second){
+			LM_ERR("Null second parameter received and use_domain set to true\n");
+			return -1;
+		}
+
+		tmp.len = first->len + second->len + 1;
+		if( tmp.len > BUF_MAXSIZE - 1 ){
+			LM_ERR("Very long user or domain");
+			return -1;
+		}
+		memcpy(aux, first->s, first->len);
+		aux[first->len] = '@';
+		memcpy(aux + first->len + 1, second->s, second->len);
+		tmp.s = aux;
+
+                crc32_uint(&tmp, &crc32_val);
+		return crc32_val % max_loc_nr + 1;
+	}else{
+		crc32_uint(first, &crc32_val);
+		LM_DBG("crc32 for %.*s is %u\n", first->len, first->s, crc32_val);
+		return crc32_val % max_loc_nr + 1;
+	}
+}
+
+
+int load_data(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int id){
+	db1_res_t *res;
+	db_row_t *row;
+	db_key_t cols[7];
+	db_key_t keys[2];
+	db_val_t key_vals[2];
+	db_op_t op[2];
+	db_key_t order;
+	int i, ret = 0;
+
+	if(!dbf || !dbh || !handle){
+		LM_ERR("NULL-Pointer in Parameter\n");
+		return -1;
+	}
+
+	memset(handle, 0, sizeof(ul_db_handle_t));
+	
+	cols[0] = &num_col;
+	cols[1] = &url_col;
+	cols[2] = &status_col;
+	cols[3] = &failover_time_col;
+	cols[4] = &spare_col;
+	cols[5] = &error_col;
+	cols[6] = &risk_group_col;
+	
+	order = &num_col;
+
+	keys[0] = &id_col;
+	op[0] = OP_EQ;
+	key_vals[0].type = DB1_INT;
+	key_vals[0].nul = 0;
+	key_vals[0].val.int_val = id;
+	
+	if(dbf->use_table(dbh, &reg_table) < 0){
+		LM_ERR("could't use table.\n");
+		return -1;
+	}
+	if(dbf->query(dbh, keys, op, key_vals, cols, 1, 7, order, &res) < 0){
+		LM_ERR("error while doing db query.\n");
+		return -1;
+	}
+	if(RES_ROW_N(res) < DB_NUM) {
+		LM_ERR("keys have too few location databases\n");
+		ret = -1;
+		goto ret;
+	}
+
+	handle->id = id;
+	
+	for(i=0; i<DB_NUM; i++) {
+		row = RES_ROWS(res) + i;
+		handle->db[i].no = (int)VAL_INT(ROW_VALUES(row));
+		if(VAL_NULL(ROW_VALUES(row) + 1)){
+			LM_ERR("Weird: Empty database URL\n");
+			ret = -1;
+			goto ret;
+		}
+		if(strlen((char *)VAL_STRING(ROW_VALUES(row) + 1)) >= (UL_DB_URL_LEN - 1)){
+			LM_ERR("weird: very large URL (%d Bytes)\n", strlen((char *)VAL_STRING(ROW_VALUES(row) + 1)) + 1);
+			ret = -1;
+			goto ret;
+		}
+		strcpy(handle->db[i].url.s, (char *)VAL_STRING(ROW_VALUES(row) + 1));
+		handle->db[i].url.len = strlen(handle->db[i].url.s);
+		handle->db[i].status = (int)VAL_INT(ROW_VALUES(row) + 2);
+		handle->db[i].failover_time = VAL_TIME (ROW_VALUES(row) + 3);
+		handle->db[i].spare = VAL_INT (ROW_VALUES(row) + 4);
+		handle->db[i].errors = VAL_INT (ROW_VALUES(row) + 5);
+		handle->db[i].rg = VAL_INT (ROW_VALUES(row) + 6);
+	}
+ret:
+	dbf->free_result(dbh, res);
+	return ret;
+}

+ 86 - 0
modules_k/p_usrloc/ul_db_handle.h

@@ -0,0 +1,86 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_check.h"
+#include "../../str.h"
+#include "../../lib/srdb1/db.h"
+
+#ifndef SP_P_USRLOC_UL_DB_HANDLE_H
+#define SP_P_USRLOC_UL_DB_HANDLE_H
+
+#define DB_NUM 1
+
+#define DB_OFF 0
+#define DB_ON 1
+#define DB_INACTIVE 2
+
+#define UL_DB_URL_LEN 260
+
+typedef struct str2s {
+	char s[UL_DB_URL_LEN];
+	int len;
+} str2;
+
+typedef struct ul_db {
+	str2 url;
+	int no;
+	time_t failover_time;
+	time_t retry;
+	int errors;
+	int status;
+	int spare;
+	int rg;
+	db1_con_t * dbh;
+	db_func_t dbf;
+}ul_db_t;
+
+typedef struct ul_db_handle {
+	unsigned int id;
+	struct check_data * check;
+	int working;
+	time_t expires;
+	int active;
+	ul_db_t db[DB_NUM];
+}ul_db_handle_t;
+
+typedef struct ul_db_handle_list {
+	ul_db_handle_t * handle;
+	struct ul_db_handle_list * next;
+}ul_db_handle_list_t;
+
+void destroy_handles(void);
+
+int refresh_handles(db_func_t * dbf, db1_con_t * dbh);
+
+int load_location_number(db_func_t * dbf, db1_con_t * dbh, int*);
+int load_handles(db_func_t * dbf, db1_con_t * dbh);
+
+ul_db_handle_t * get_handle(db_func_t * dbf, db1_con_t * dbh, str * first, str * second);
+
+int load_data(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle, int id);
+
+int refresh_handle(ul_db_handle_t * handle, ul_db_handle_t * new_data, int error_handling);
+
+ul_db_t * get_db_by_num(ul_db_handle_t * handle, int no);
+
+int check_handle(db_func_t * dbf, db1_con_t * dbh, ul_db_handle_t * handle);
+
+#endif

+ 28 - 0
modules_k/p_usrloc/ul_db_ins.c

@@ -0,0 +1,28 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_ins.h"
+#include "ul_db_form_query.h"
+
+int db_insert(ul_db_handle_t * handle, str * table,
+              db_key_t* _k, db_val_t* _v, int _n) {
+	return db_submit_query(UL_DB_INS, handle, table, _k, NULL, _v, NULL, NULL, _n, 0);
+}

+ 30 - 0
modules_k/p_usrloc/ul_db_ins.h

@@ -0,0 +1,30 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_DB_INS_H
+#define SP_P_USRLOC_DB_INS_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+int db_insert(ul_db_handle_t * handle, str * table,  db_key_t* _k, db_val_t* _v, int _n);
+
+#endif

+ 28 - 0
modules_k/p_usrloc/ul_db_ins_upd.c

@@ -0,0 +1,28 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_ins_upd.h"
+#include "ul_db_form_query.h"
+
+int db_insert_update(ul_db_handle_t * handle,
+                     str * table, db_key_t* _k, db_val_t* _v, int _n) {
+	return db_submit_query(UL_DB_INS_UPD, handle, table, _k, NULL, _v, NULL, NULL, _n, 0);
+}

+ 30 - 0
modules_k/p_usrloc/ul_db_ins_upd.h

@@ -0,0 +1,30 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_DB_INS_UPD_H
+#define SP_P_USRLOC_DB_INS_UPD_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+int db_insert_update(ul_db_handle_t * handle, str * table, db_key_t* _k, db_val_t* _v, int _n);
+
+#endif

+ 475 - 0
modules_k/p_usrloc/ul_db_layer.c

@@ -0,0 +1,475 @@
+#include "../../lib/srdb1/db.h"
+#include "ul_db.h"
+#include "ul_mod.h"
+#include "ul_db_layer.h"
+#include "ul_db_api.h"
+
+typedef struct res_list {
+	db1_con_t ** h;
+	db1_res_t * r;
+	struct res_list * next;
+}
+res_list_t;
+
+static void add_res(db1_res_t * _r, db1_con_t ** _h);
+static db1_con_t ** get_handle_by_res(db1_res_t * _r);
+static void drop_res(db1_res_t * _r);
+
+static ul_db_api_t p_ul_dbf;
+static db_func_t dbf;
+
+static res_list_t * used = NULL;
+static res_list_t * unused = NULL;
+
+static ul_domain_db_list_t * domain_db_list = NULL;
+
+int ul_db_layer_init(void) {
+	if(bind_ul_db(&p_ul_dbf) < 0) {
+		LM_ERR("could not bind sp_ul_db_api.\n");
+		return -1;
+	}
+	if(db_bind_mod(&default_db_url, &dbf) < 0) {
+		LM_ERR("could not bind db.\n");
+		return -1;
+	}
+	return 0;
+}
+
+void ul_db_layer_destroy(void) {
+	res_list_t * tmp, * del;
+	tmp = used;
+	while(tmp) {
+		del = tmp;
+		tmp = tmp->next;
+		pkg_free(del);
+	}
+	tmp = unused;
+	while(tmp) {
+		del = tmp;
+		tmp = tmp->next;
+		pkg_free(del);
+	}
+	return;
+}
+
+int ul_db_layer_single_connect(udomain_t * domain, str * url) {
+	if((domain->dbh = dbf.init(url)) == NULL){
+		return -1;
+	}
+	return 1;
+}
+
+int ul_db_layer_insert(udomain_t * domain, str * user, str * sipdomain,
+                       db_key_t* _k, db_val_t* _v, int _n) {
+	ul_domain_db_t * d;
+	switch(domain->dbt) {
+			case DB_TYPE_CLUSTER: return p_ul_dbf.insert(domain->name, user, sipdomain, _k, _v, _n);
+			case DB_TYPE_SINGLE: if(!domain->dbh){
+				if((d = ul_find_domain(domain->name->s)) == 0){
+					return -1;
+				}
+				if(ul_db_layer_single_connect(domain, &d->url) < 0){
+					return -1;
+				}
+			}
+			if(dbf.use_table(domain->dbh, domain->name) < 0) {
+				return -1;
+			}
+			return dbf.insert(domain->dbh, _k, _v, _n);
+			default: return -1;
+	}
+}
+
+int ul_db_layer_update(udomain_t * domain, str * user, str * sipdomain, db_key_t* _k, db_op_t* _o, db_val_t* _v,
+	         db_key_t* _uk, db_val_t* _uv, int _n, int _un) {
+	ul_domain_db_t * d;
+	switch(domain->dbt) {
+			case DB_TYPE_CLUSTER: return p_ul_dbf.update(domain->name, user, sipdomain, _k, _o, _v, _uk, _uv, _n, _un);
+			case DB_TYPE_SINGLE: if(!domain->dbh){
+				if((d = ul_find_domain(domain->name->s)) == 0){
+					return -1;
+				}
+				if(ul_db_layer_single_connect(domain, &d->url) < 0){
+					return -1;
+				}
+			}
+			if(dbf.use_table(domain->dbh, domain->name) < 0) {
+				return -1;
+			}
+			return dbf.update(domain->dbh, _k, _o, _v, _uk, _uv, _n, _un);
+			default: return -1;
+	}
+}
+
+int ul_db_layer_insert_update(udomain_t * domain, str * user, str * sipdomain,
+                              db_key_t* _k,	db_val_t* _v, int _n) {
+	ul_domain_db_t * d;
+	switch(domain->dbt) {
+			case DB_TYPE_CLUSTER: return p_ul_dbf.insert_update(domain->name, user, sipdomain, _k, _v, _n);;
+			case DB_TYPE_SINGLE: if(!domain->dbh){
+				if((d = ul_find_domain(domain->name->s)) == 0){
+					return -1;
+				}
+				if(ul_db_layer_single_connect(domain, &d->url) < 0){
+					return -1;
+				}
+			}
+				if(dbf.use_table(domain->dbh, domain->name) < 0) {
+				return -1;
+			}
+			return dbf.insert_update(domain->dbh, _k, _v, _n);
+			default: return -1;
+	}
+}
+
+int ul_db_layer_delete(udomain_t * domain, str * user, str * sipdomain,
+                       db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n) {
+	ul_domain_db_t * d;
+	switch(domain->dbt) {
+			case DB_TYPE_CLUSTER: return p_ul_dbf.delete(domain->name, user, sipdomain, _k, _o, _v, _n);
+			case DB_TYPE_SINGLE: if(!domain->dbh){
+				if((d = ul_find_domain(domain->name->s)) == 0){
+					return -1;
+				}
+				if(ul_db_layer_single_connect(domain, &d->url) < 0){
+					return -1;
+				}
+			}
+				if(dbf.use_table(domain->dbh, domain->name) < 0) {
+				return -1;
+			}
+			return dbf.delete(domain->dbh, _k, _o, _v, _n);
+			default: return -1;
+	}
+}
+
+int ul_db_layer_query(udomain_t * domain, str * user, str * sipdomain,
+                      db_key_t* _k, db_op_t* _op,	db_val_t* _v, db_key_t* _c,
+                      int _n, int _nc, db_key_t _o, db1_res_t** _r) {
+	ul_domain_db_t * d;
+	db1_con_t ** h;
+	int ret;
+	switch(domain->dbt) {
+			case DB_TYPE_CLUSTER:{ 
+			LM_ERR("CALLING CLUSTER QUERY\n");
+			if((ret = p_ul_dbf.query(domain->name, user, sipdomain, &h, _k, _op, _v, _c, _n, _nc, _o, _r)) < 0 || (!_r)) {
+				return -1;
+			}
+			add_res(*_r, h);
+			return ret;
+			}
+			case DB_TYPE_SINGLE: if(!domain->dbh){
+				if((d = ul_find_domain(domain->name->s)) == 0){
+					return -1;
+				}
+				if(ul_db_layer_single_connect(domain, &d->url) < 0){
+					return -1;
+				}
+			}
+				if(dbf.use_table(domain->dbh, domain->name) < 0) {
+				return -1;
+			}
+			return dbf.query(domain->dbh, _k, _op, _v, _c, _n, _nc, _o, _r);
+			default: return -1;
+	}
+}
+
+int ul_db_layer_raw_query(udomain_t * domain, str* _s, db1_res_t** _r) {
+	ul_domain_db_t * d;
+	switch(domain->dbt) {
+			case DB_TYPE_CLUSTER: {
+				return -1; // not useful in this mode
+			}
+			case DB_TYPE_SINGLE: if(!domain->dbh){
+				if((d = ul_find_domain(domain->name->s)) == 0){
+					return -1;
+				}
+				if(ul_db_layer_single_connect(domain, &d->url) < 0){
+					return -1;
+				}
+			}
+			if(dbf.use_table(domain->dbh, domain->name) < 0) {
+				return -1;
+			}
+			return dbf.raw_query(domain->dbh, _s, _r);
+			default: return -1;
+	}
+}
+
+int ul_db_layer_free_result(udomain_t * domain, db1_res_t * res) {
+	db1_con_t ** h;
+	int ret;
+	switch(domain->dbt) {
+			case DB_TYPE_CLUSTER: if((h = get_handle_by_res(res)) == NULL) {
+				return -1;
+			}
+			ret = p_ul_dbf.free_result(h, res);
+			drop_res(res);
+			return ret;
+			case DB_TYPE_SINGLE: return dbf.free_result(domain->dbh, res);
+			default: return -1;
+	}
+}
+
+int ul_add_domain_db(str * d, int t, str * url) {
+	ul_domain_db_list_t * new_d = NULL;
+	LM_DBG("%.*s, type: %s\n", d->len, d->s,
+			t==DB_TYPE_SINGLE ? "SINGLE" : "CLUSTER");
+	if((new_d = pkg_malloc(sizeof(ul_domain_db_list_t))) == NULL) {
+		return -1;
+	}
+	memset(new_d, 0, sizeof(ul_domain_db_list_t));
+	if(!d){
+		return -1;
+	}
+	if(!d->s){
+		return -1;		
+	}
+	if((new_d->domain.name.s = pkg_malloc(d->len + 1)) == NULL) {
+		return -1;
+	}
+	if(t == DB_TYPE_SINGLE) {
+		if(url) {
+			LM_DBG("url: %.*s", url->len, url->s);
+			if((new_d->domain.url.s = pkg_malloc(url->len + 1)) == NULL) {
+				return -1;
+			}
+			strncpy(new_d->domain.url.s, url->s, url->len);
+                       new_d->domain.url.s[url->len] = '\0';
+			new_d->domain.url.len = url->len;
+		} else {
+			if((new_d->domain.url.s = pkg_malloc(default_db_url.len + 1)) == NULL) {
+				return -1;
+			}
+			strcpy(new_d->domain.url.s, default_db_url.s);
+			new_d->domain.url.len = default_db_url.len;
+		}
+	}
+	strncpy(new_d->domain.name.s, d->s, d->len);
+	new_d->domain.name.len = d->len;
+	new_d->domain.dbt = t;
+	new_d->next = domain_db_list;
+	domain_db_list = new_d;
+	return 1;
+}
+
+ul_domain_db_t * ul_find_domain(const char * s) {
+	ul_domain_db_list_t * tmp;
+	str d;
+	if(!domain_db_list){
+		if(parse_domain_db(&domain_db) < 0){
+			LM_ERR("could not parse domain parameter.\n");
+			return NULL;
+		}
+	}
+	tmp = domain_db_list;
+	while(tmp){
+		LM_DBG("searched domain: %s, actual domain: %.*s, length: %i, type: %s\n", 
+			s, tmp->domain.name.len, tmp->domain.name.s, tmp->domain.name.len, 
+			tmp->domain.dbt==DB_TYPE_SINGLE ? "SINGLE" : "CLUSTER");
+		if((strlen(s) == tmp->domain.name.len) && (memcmp(s, tmp->domain.name.s, tmp->domain.name.len) == 0)){
+			return &tmp->domain;
+		}
+		tmp = tmp->next;
+	}
+	if((d.s = pkg_malloc(strlen(s) + 1)) == NULL){
+		return NULL;
+	}
+	strcpy(d.s, s);
+	d.len = strlen(s);
+	if(ul_add_domain_db(&d, default_dbt, &default_db_url)){
+		pkg_free(d.s);
+		return ul_find_domain(s);
+	}
+	pkg_free(d.s);
+	return NULL;
+}
+
+void free_all_udomains(void) {
+	ul_domain_db_list_t * tmp, * del;
+	tmp = domain_db_list;
+	while(tmp){
+		del = tmp;
+		tmp = tmp->next;
+		pkg_free(del->domain.name.s);
+		if(del->domain.dbt == DB_TYPE_SINGLE){
+			pkg_free(del->domain.url.s);
+		}
+		pkg_free(del);
+	}
+	return;
+}
+
+int parse_domain_db(str * _d) {
+#define STATE_START 0
+#define STATE_DOMAIN 1
+#define STATE_TYPE 2
+#define STATE_URL 3
+	char * domains;
+	char * end;
+	str d = {NULL, 0}, u = {NULL, 0}, t = {NULL, 0};
+	int type = 0;
+	int state;
+
+	if (_d->len==0) {
+		return -1;
+	}
+	domains = _d->s;
+	end = domains + _d->len;
+
+	state = STATE_START;
+	while(domains <= end) {
+		switch(*domains) {
+			case '=':
+				switch(state) {
+					case STATE_START: return -1;
+					case STATE_DOMAIN: 
+						LM_DBG("found domain %.*s\n", d.len, d.s);
+						state = STATE_TYPE;
+						t.s = domains + 1;
+						t.len = 0;
+						break;
+						case STATE_TYPE: return -1;
+						case STATE_URL: return -1;
+						default: return -1;
+				}
+				break;
+			case ';':
+				switch(state) {
+					case STATE_START: return 1;
+					case STATE_DOMAIN: return -1;
+					case STATE_TYPE: 
+						LM_DBG("found type %.*s\n", t.len, t.s);
+						if(strncmp(t.s, DB_TYPE_CLUSTER_STR, strlen(DB_TYPE_CLUSTER_STR)) == 0) {
+							type = DB_TYPE_CLUSTER;
+						} else {
+							type = DB_TYPE_SINGLE;
+						}
+						state = STATE_URL;
+						u.s = domains + 1;
+						u.len = 0;
+						break;
+						case STATE_URL: return -1;
+						break;
+						default: break;
+				}
+				break;
+			case ',':
+				switch(state) {
+					case STATE_START: return -1;
+					case STATE_DOMAIN: return -1;
+					case STATE_TYPE: 
+						LM_DBG("found type %.*s\n", t.len, t.s);
+						if(strncmp(t.s, DB_TYPE_CLUSTER_STR, strlen(DB_TYPE_CLUSTER_STR)) == 0) {
+							type = DB_TYPE_CLUSTER;
+						} else {
+							type = DB_TYPE_SINGLE;
+						}
+						ul_add_domain_db(&d, type, NULL);
+						state = STATE_START;
+						break;
+					case STATE_URL: 
+						LM_DBG("found url %.*s\n", u.len, u.s);
+						state = STATE_START;
+						ul_add_domain_db(&d, type, &u);	
+						break;
+						default: return -1;
+				}
+				break;
+			case '\0':
+				switch(state) {
+					case STATE_START:
+						return 1;
+					case STATE_DOMAIN:
+						return -1;
+					case STATE_TYPE:
+						LM_DBG("found type %.*s\n", t.len, t.s);
+						if(strncmp(t.s, DB_TYPE_CLUSTER_STR, strlen(DB_TYPE_CLUSTER_STR)) == 0) {
+							type = DB_TYPE_CLUSTER;
+						} else {
+							type = DB_TYPE_SINGLE;
+						}
+						ul_add_domain_db(&d, type, NULL);
+						return 1;
+					case STATE_URL:
+						LM_DBG("found url %.*s\n", u.len, u.s);
+						ul_add_domain_db(&d, type, &u);
+						return 1;
+						default: return -1;
+				}
+				break;
+			default:
+				switch(state) {
+					case STATE_START:
+						state = STATE_DOMAIN;
+						d.s = domains;
+						d.len = 1;
+						break;
+					case STATE_DOMAIN:
+						d.len++;
+						break;
+					case STATE_TYPE:
+						t.len++;
+						break;
+					case STATE_URL:
+						u.len++;
+						break;
+						default: break;
+				}
+				break;
+		}
+		domains++;
+	}
+	return 1;
+}
+
+static void add_res(db1_res_t * _r, db1_con_t ** _h) {
+	res_list_t * new_res;
+	if(!unused) {
+		if((new_res = pkg_malloc(sizeof(res_list_t))) == NULL) {
+			return;
+		}
+		memset(new_res, 0, sizeof(res_list_t));
+	} else {
+		new_res = unused;
+		unused = unused->next;
+	}
+	new_res->h = _h;
+	new_res->r = _r;
+	new_res->next = used;
+	used = new_res;
+	return;
+}
+
+static db1_con_t ** get_handle_by_res(db1_res_t * _r) {
+	res_list_t * tmp = used;
+	while(tmp) {
+		if(_r == tmp->r) {
+			return tmp->h;
+		}
+		tmp = tmp->next;
+	}
+	return NULL;
+}
+
+static void drop_res(db1_res_t * _r) {
+	res_list_t * prev = NULL;
+	res_list_t * tmp;
+	tmp = used;
+	while(tmp) {
+		if(_r == tmp->r) {
+			if(prev==NULL) {
+				used = tmp->next;
+			} else {
+				prev->next = tmp->next;
+			}
+			tmp->next = unused;
+			unused = tmp;
+			return;
+		}
+		prev = tmp;
+		tmp = tmp->next;
+	}
+	return;
+}
+

+ 59 - 0
modules_k/p_usrloc/ul_db_layer.h

@@ -0,0 +1,59 @@
+#ifndef  UL_DB_LAYER_H
+#define UL_DB_LAYER_H
+
+#include "../../lib/srdb1/db.h"
+#include "usrloc.h"
+
+#define DB_TYPE_CLUSTER 0
+#define DB_TYPE_CLUSTER_STR "cluster"
+#define DB_TYPE_SINGLE 1
+#define DB_TYPE_SINGLE_STR "single"
+
+typedef struct ul_domain_db {
+	str name;
+	str url;
+	int dbt;
+	struct ul_domain_db_list * next;
+} ul_domain_db_t;
+
+typedef struct ul_domain_db_list {
+	ul_domain_db_t domain;
+	struct ul_domain_db_list * next;
+} ul_domain_db_list_t;
+
+int ul_db_layer_init();
+
+void ul_db_layer_destroy();
+
+int ul_db_layer_single_connect(udomain_t * domain, str * url);
+
+int ul_db_layer_insert(udomain_t * domain, str * user, str * sipdomain, 
+				 db_key_t* _k, db_val_t* _v, int _n);
+
+int ul_db_layer_update(udomain_t * domain, str * user, str * sipdomain, db_key_t* _k, db_op_t* _o, db_val_t* _v,
+	         db_key_t* _uk, db_val_t* _uv, int _n, int _un);
+
+int ul_db_layer_insert_update(udomain_t * domain, str * user, str * sipdomain, 
+						db_key_t* _k,	db_val_t* _v, int _n);
+
+int ul_db_layer_delete(udomain_t * domain, str * user, str * sipdomain, 
+				 db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
+
+int ul_db_layer_query(udomain_t * domain, str * user, str * sipdomain,
+				db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, 
+				int _n, int _nc, db_key_t _o, db1_res_t** _r);
+
+int ul_db_layer_raw_query(udomain_t * domain, str* _s, db1_res_t** _r);
+
+int ul_db_layer_fetch_result(udomain_t * domain, db1_res_t** _r, int _n);
+
+int ul_db_layer_free_result(udomain_t * domain, db1_res_t * res);
+
+int ul_add_domain_db(str * d, int t, str * url);
+
+ul_domain_db_t * ul_find_domain(const char * s);
+
+int parse_domain_db(str * _d);
+
+#endif
+

+ 112 - 0
modules_k/p_usrloc/ul_db_query.c

@@ -0,0 +1,112 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_query.h"
+#include "ul_db.h"
+
+
+static int order_dbs(ul_db_handle_t * handle, int order[]);
+
+static int db_exec_query(db_func_t * dbf, db1_con_t * dbh, str * table,
+                         db_key_t* _k, db_op_t * _op, db_val_t * _v,
+                         db_key_t * _c, int _n, int _nc, db_key_t _o,
+                         db1_res_t ** _r);
+
+int db_query(ul_db_handle_t * handle, db1_con_t *** _r_h, db_func_t ** _r_f,
+             str * table, db_key_t* _k, db_op_t * _op, db_val_t * _v,
+             db_key_t * _c, int _n, int _nc, db_key_t _o, db1_res_t ** _r, int rw) {
+	int order[DB_NUM];
+	int i;
+	int err[DB_NUM];
+	int ret = -1;
+	order_dbs(handle, order);
+	memset(err, 0 , sizeof(int) * DB_NUM);
+
+	if(!handle || !table || !table->s || !_r_h) {
+		LM_ERR("NULL pointer in parameter.\n");
+		return -1;
+	}
+	i = 0;
+	do {
+		LM_DBG("now trying id %i, db %i.\n", handle->id, handle->db[order[i]].no);
+		if(handle->db[order[i]].status == DB_ON) {
+			if((ret = db_exec_query(&handle->db[order[i]].dbf, handle->db[order[i]].dbh, table, _k, _op, _v, _c, _n, _nc, _o, _r)) < 0) {
+				LM_ERR("could not query table %.*s error on id %i, db %i.\n", table->len, table->s, handle->id, handle->db[order[i]].no);
+				if(rw) {
+					if(err[i] == 0 && handle->db[order[i]].status == DB_ON) {
+						if(db_handle_error(handle, handle->db[order[i]].no) < 0) {
+							LM_ERR("could not handle error on id %i, db %i.\n", handle->id, handle->db[order[i]].no);
+						} else {
+							err[i] = 1;
+							i--;
+						}
+					}
+				}
+			}
+		}
+		i++;
+	} while((ret < 0) && (i < DB_NUM));
+	i--;
+	LM_DBG("returned handle is for id %i, db %i\n", handle->id, handle->db[order[i]].no);
+	*_r_h = &handle->db[order[i]].dbh;
+	*_r_f = &handle->db[order[i]].dbf;
+	return ret;
+}
+
+static int order_dbs(ul_db_handle_t * handle, int order[]) {
+	int i,j, tmp;
+	for(i=0; i<DB_NUM; i++) {
+		order[i] = i;
+	}
+	for(i=0; i<DB_NUM; i++) {
+		for(j=i+1; j<DB_NUM; j++) {
+			if((handle->db[i].status == DB_OFF || handle->db[i].status == DB_INACTIVE) && (handle->db[j].status == DB_ON)) {
+				tmp = order[i];
+				order[i] = order[j];
+				order[j] = tmp;
+			} else if(handle->db[i].failover_time > handle->db[j].failover_time) {
+				tmp = order[i];
+				order[i] = order[j];
+				order[j] = tmp;
+			}
+		}
+	}
+	return 0;
+}
+
+static int db_exec_query(db_func_t * dbf, db1_con_t * dbh, str * table,
+                         db_key_t* _k, db_op_t * _op, db_val_t * _v,
+                         db_key_t * _c, int _n, int _nc, db_key_t _o,
+                         db1_res_t ** _r) {
+	if(!dbf || !dbh || !table || !table->s) {
+		LM_ERR("NULL pointer in parameter.\n");
+		return -1;
+	}
+	if(dbf->use_table(dbh, table) < 0) {
+		LM_ERR("could not use table %.*s.\n", table->len, table->s);
+		return -1;
+	}
+	if(dbf->query(dbh, _k, _op, _v, _c, _n, _nc, _o, _r) < 0) {
+		LM_ERR("could not query table %.*s.\n", table->len, table->s);
+		return -1;
+	}
+	return 0;
+}

+ 31 - 0
modules_k/p_usrloc/ul_db_query.h

@@ -0,0 +1,31 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_DB_QUERY_H
+#define SP_P_USRLOC_DB_QUERY_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+int db_query(ul_db_handle_t * handle, db1_con_t *** _r_h, db_func_t ** _r_f, str * table, db_key_t* _k, db_op_t * _op, db_val_t * _v,
+                db_key_t * _c, int _n, int _nc, db_key_t _o, db1_res_t ** _r, int rw);
+
+#endif

+ 28 - 0
modules_k/p_usrloc/ul_db_repl.c

@@ -0,0 +1,28 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_repl.h"
+#include "ul_db_form_query.h"
+
+int db_replace(ul_db_handle_t * handle, str * table,
+              db_key_t* _k, db_val_t* _v, int _n) {
+	return db_submit_query(UL_DB_REPL, handle, table, _k, NULL, _v, NULL, NULL, _n, 0);
+}

+ 30 - 0
modules_k/p_usrloc/ul_db_repl.h

@@ -0,0 +1,30 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_DB_REPL_H
+#define SP_P_USRLOC_DB_REPL_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+int db_replace(ul_db_handle_t * handle, str * table,  db_key_t* _k, db_val_t* _v, int _n);
+
+#endif

+ 223 - 0
modules_k/p_usrloc/ul_db_tran.c

@@ -0,0 +1,223 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_tran.h"
+#include "ul_db.h"
+#include "ul_mod.h"
+
+static str autocommit_off = str_init("SET AUTOCOMMIT=0");
+static str start_transaction = str_init("START TRANSACTION");
+static str commit = str_init("COMMIT");
+static str autocommit_on = str_init("SET AUTOCOMMIT=1");
+static str rollback = str_init("ROLLBACK");
+
+static int submit_tran_start(db_func_t * dbf, db1_con_t * dbh);
+static int submit_tran_commit(db_func_t * dbf, db1_con_t * dbh);
+static int submit_tran_rollback(db_func_t * dbf, db1_con_t * dbh);
+
+int ul_db_tran_start(ul_db_handle_t * handle, int working[]) {
+	int i;
+	int errors = 0;
+	int w = 0;
+
+	if(!handle || !working) {
+		LM_ERR("NULL pointer in parameter.\n");
+		return -1;
+	}
+
+	for(i=0; i<DB_NUM; i++) {
+		if(handle->db[i].status == DB_ON) {
+			if(submit_tran_start(&handle->db[i].dbf, handle->db[i].dbh) < 0) {
+				LM_ERR("error while starting "
+				    "transaction on id %i, db %i.\n", handle->id, handle->db[i].no);
+				if(db_handle_error(handle, handle->db[i].no) < 0) {
+					LM_ERR("error during handling error "
+					    "on id %i on db %i, trying again.\n", handle->id, handle->db[i].no);
+					errors++;
+				} else {
+					if(submit_tran_start(&handle->db[i].dbf, handle->db[i].dbh) < 0) {
+						LM_ERR("error while starting "
+						    "transaction on id %i, db %i.\n", handle->id, handle->db[i].no);
+						errors++;
+					}
+				}
+
+			} else {
+				working[i] = 1;
+				w++;
+			}
+		}
+	}
+	if((errors > 0) || (w < handle->working)) {
+		return -1;
+	}
+	return 0;
+}
+
+int ul_db_tran_commit(ul_db_handle_t * handle, int working[]) {
+	int i;
+	int errors = 0;
+	int w = 0;
+
+	if(!handle || !working) {
+		LM_ERR("NULL pointer in parameter.\n");
+		return -1;
+	}
+
+	for(i=0; i<DB_NUM; i++) {
+		if((handle->db[i].status == DB_ON) && (working[i])) {
+			if(submit_tran_commit(&handle->db[i].dbf, handle->db[i].dbh) < 0) {
+				LM_ERR("error while commiting "
+				    "transaction on id %i, db %i.\n", handle->id, handle->db[i].no);
+				if(db_handle_error(handle, handle->db[i].no) < 0) {
+					LM_ERR("error during handling error "
+					    "on id %i on db %i, trying again.\n", handle->id, handle->db[i].no);
+				}
+				errors++;
+			} else {
+				w++;
+			}
+		}
+	}
+	if((errors > 0) || (w < get_working_sum(working, DB_NUM))) {
+		return -1;
+	}
+	return 0;
+}
+
+int ul_db_tran_rollback(ul_db_handle_t * handle, int working[]) {
+	int i;
+	int errors = 0;
+	int w = 0;
+
+	if(!handle || !working) {
+		LM_ERR("NULL pointer in parameter.\n");
+		return -1;
+	}
+
+	for(i=0; i<DB_NUM; i++) {
+		if((handle->db[i].status == DB_ON) && (working[i])) {
+			if(submit_tran_rollback(&handle->db[i].dbf, handle->db[i].dbh) < 0) {
+				LM_ERR("error while rolling back "
+				    "transaction on id %i, db %i.\n", handle->id, handle->db[i].no);
+				errors++;
+			} else {
+				w++;
+			}
+		}
+	}
+	if((errors > 0) || (w < get_working_sum(working, DB_NUM))) {
+		return -1;
+	}
+	return 0;
+}
+
+static int submit_tran_start(db_func_t * dbf, db1_con_t * dbh) {
+	int errors = 0;
+	str tmp;
+	if(dbh) {
+		if(dbf->raw_query(dbh, &autocommit_off, NULL) < 0) {
+			LM_ERR("error while turning off "
+			    "autocommit.\n");
+			errors++;
+		}
+		tmp.s  = isolation_level;
+		tmp.len = strlen(isolation_level);
+		if(dbf->raw_query(dbh, &tmp, NULL) < 0) {
+			LM_ERR("error while setting "
+			    "isolation level.\n");
+			errors++;
+		}
+		if(dbf->raw_query(dbh, &start_transaction, NULL) < 0) {
+			LM_ERR("error while starting "
+			    "transaction.\n");
+			errors++;
+		}
+	} else {
+		LM_ERR("no db handle.\n");
+		return -1;
+	}
+	if(errors > 0) {
+		return -1;
+	}
+	return 0;
+}
+
+static int submit_tran_commit(db_func_t * dbf, db1_con_t * dbh) {
+	int errors = 0;
+
+	if(dbh) {
+		if(dbf->raw_query(dbh, &commit, NULL) < 0) {
+			LM_ERR("error during commit.\n");
+			errors++;
+		}
+		if(dbf->raw_query(dbh, &autocommit_on, NULL) < 0) {
+			LM_ERR("error while turning "
+			    "on autocommit.\n");
+			errors++;
+		}
+	} else {
+		LM_ERR("no db handle.\n");
+		return -1;
+	}
+
+	if(errors > 0) {
+		return -1;
+	}
+	return 0;
+}
+
+static int submit_tran_rollback(db_func_t * dbf, db1_con_t * dbh) {
+	int errors = 0;
+
+	if(dbh) {
+		if(dbf->raw_query(dbh, &rollback, NULL) < 0) {
+			LM_ERR("error during "
+			    "rollback.\n");
+			errors++;
+		}
+		if(dbf->raw_query(dbh, &autocommit_on, NULL) < 0) {
+			LM_ERR("error while "
+			    "turning on autocommit.\n");
+			errors++;
+		}
+	} else {
+		LM_ERR("no db handle.\n");
+		return -1;
+	}
+
+	if(errors > 0) {
+		return -1;
+	}
+	return 0;
+}
+
+int get_working_sum(int working[], int no) {
+	int i;
+	int sum = 0;
+	if(!working) {
+		return -1;
+	}
+	for(i=0; i<no; i++) {
+		sum += working[i];
+	}
+	return sum;
+}

+ 36 - 0
modules_k/p_usrloc/ul_db_tran.h

@@ -0,0 +1,36 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_UL_DB_TRAN_H
+#define SP_P_USRLOC_UL_DB_TRAN_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+int ul_db_tran_start(ul_db_handle_t * handle, int working[]);
+
+int ul_db_tran_commit(ul_db_handle_t * handle, int working[]);
+
+int ul_db_tran_rollback(ul_db_handle_t * handle, int working[]);
+
+int get_working_sum(int working[], int no);
+
+#endif

+ 29 - 0
modules_k/p_usrloc/ul_db_upd.c

@@ -0,0 +1,29 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "ul_db_upd.h"
+#include "ul_db_form_query.h"
+
+int db_update(ul_db_handle_t * handle, str * table,
+              db_key_t* _k, db_op_t* _o, db_val_t* _v, db_key_t* _uk,
+              db_val_t* _uv, int _n, int _un) {
+	return db_submit_query(UL_DB_UPD, handle, table, _k, _o, _v, _uk, _uv, _n, _un);
+}

+ 31 - 0
modules_k/p_usrloc/ul_db_upd.h

@@ -0,0 +1,31 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_P_USRLOC_DB_UPD_H
+#define SP_P_USRLOC_DB_UPD_H
+
+#include "../../lib/srdb1/db.h"
+#include "ul_db_handle.h"
+
+int db_update(ul_db_handle_t * handle, str * table, db_key_t* _k, db_op_t* _o,
+              db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un);
+
+#endif

+ 213 - 0
modules_k/p_usrloc/ul_db_watch.c

@@ -0,0 +1,213 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "../../timer.h"
+#include "../../timer_proc.h"
+#include "../../sr_module.h"
+#include "ul_db_watch.h"
+#include "ul_db.h"
+#include "ul_mod.h"
+
+typedef struct ul_db_watch_list {
+	int id;
+	int active;
+	struct ul_db_watch_list * next;
+} ul_db_watch_list_t;
+
+ul_db_watch_list_t ** list = NULL;
+
+gen_lock_t * list_lock;
+
+static ul_db_handle_list_t * handles;
+
+void check_dbs(unsigned int ticks, void *param);
+
+static int init_watch_db_list(void);
+
+int ul_db_watch_init(void){
+	if(init_watch_db_list() < 0){
+		return -1;
+	}
+	if((list = shm_malloc(sizeof(ul_db_watch_list_t *))) == NULL){
+		LM_ERR("couldn't allocate shared memory.\n");
+		return -1;
+	}
+	*list = NULL;
+	return 0;
+}
+
+int init_db_check(void){
+	int ret;
+	if(db_master_write){
+		LM_INFO("start timer, interval %i seconds\n", retry_interval);
+		ret = fork_dummy_timer(PROC_TIMER, "TIMER WATCH", 1, check_dbs, NULL, retry_interval);
+	} else {
+		ret = 0;
+	}
+	return ret;
+}
+
+void ul_db_watch_destroy(void){
+	ul_db_watch_list_t * del;
+	ul_db_handle_list_t * del2;
+	if(list_lock){
+		lock_destroy(list_lock);
+		lock_dealloc(list_lock);
+		list_lock = NULL;
+	}
+	if(list){
+		while(list && *list){
+			del = *list;
+			*list = (*list)->next;
+			shm_free(del);
+		}
+		shm_free(list);
+		list = NULL;
+	}
+	while(handles){
+		del2 = handles;
+		handles = handles->next;
+		pkg_free(del2);
+	}
+	return;
+}
+
+void check_dbs(unsigned int ticks, void *param){
+	LM_DBG("check availability of databases");
+	ul_db_watch_list_t * tmp;
+	ul_db_handle_list_t * tmp2, * new_element;
+	int found;
+	int i;
+	
+	if(!list_lock){
+		return;
+	}
+	lock_get(list_lock);	
+	tmp = *list;
+	while(tmp){
+		tmp2 = handles;
+		found = 0;
+		while(tmp2){
+			if(tmp2->handle->id == tmp->id){
+				found = 1;
+				if(tmp->active){
+					LM_INFO("handle %i found, check it\n", tmp->id);
+					tmp2->handle->active = 1;
+					ul_db_check(tmp2->handle);
+				} else if (tmp2->handle->active) {
+					for(i=0; i<DB_NUM; i++){
+						if(tmp2->handle->db[i].dbh){
+							tmp2->handle->db[i].dbf.close(tmp2->handle->db[i].dbh);
+							tmp2->handle->db[i].dbh = NULL;
+						}
+					}
+					tmp2->handle->active = 0;
+				}
+			}
+			tmp2 = tmp2->next;
+		}
+		if(!found){
+			LM_NOTICE("handle %i not found, create it\n", tmp->id);
+			if((new_element = pkg_malloc(sizeof(ul_db_handle_list_t))) == NULL){
+				LM_ERR("couldn't allocate private memory\n");
+				lock_release(list_lock);
+				return;
+			}
+			memset(new_element, 0, sizeof(ul_db_handle_list_t));
+			if((new_element->handle = pkg_malloc(sizeof(ul_db_handle_t))) == NULL){
+				LM_ERR("couldn't allocate private memory\n");
+				pkg_free(new_element);
+				lock_release(list_lock);
+				return;
+			}
+			memset(new_element->handle, 0, sizeof(ul_db_handle_t));
+			new_element->next = handles;
+			handles = new_element;
+			handles->handle->id = tmp->id;
+			ul_db_check(handles->handle);
+		}
+		tmp = tmp->next;
+		i++;
+	}
+	lock_release(list_lock);
+}
+
+int ul_register_watch_db(int id){
+	ul_db_watch_list_t * new_id = NULL, * tmp;
+	if(!list_lock){
+		if(init_watch_db_list() < 0){
+			return -1;
+		}
+	}
+	lock_get(list_lock);
+	tmp = *list;
+	while(tmp){
+		if(tmp->id == id){
+			tmp->active = 1;
+			lock_release(list_lock);
+			return 0;
+		}
+		tmp = tmp->next;
+	}
+	if((new_id = shm_malloc(sizeof(ul_db_watch_list_t))) == NULL){
+		LM_ERR("couldn't allocate shared memory\n");
+		lock_release(list_lock);
+		return -1;
+	}
+	memset(new_id, 0, sizeof(ul_db_watch_list_t));
+	new_id->active = 1;
+	new_id->next = *list;
+	new_id->id = id;
+	*list = new_id;
+	lock_release(list_lock);
+	return 0;
+}
+
+int ul_unregister_watch_db(int id){
+	ul_db_watch_list_t * tmp;
+	if(!list_lock){
+		return 0;
+	}
+	lock_get(list_lock);
+	tmp = *list;
+	while(tmp){
+		if(tmp->id == id){
+			tmp->active = 0;
+			lock_release(list_lock);
+			return 0;
+		}
+		tmp = tmp->next;
+	}
+	lock_release(list_lock);
+	return 0;
+}
+
+static int init_watch_db_list(void){
+	if((list_lock = lock_alloc()) == NULL){
+		LM_ERR("could not allocate lock\n");
+		return -1;
+	}
+	if(lock_init(list_lock) == NULL){
+		LM_ERR("could not initialise lock\n");
+		return -1;
+	}
+	return 0;
+}

+ 38 - 0
modules_k/p_usrloc/ul_db_watch.h

@@ -0,0 +1,38 @@
+/* sp-ul_db module
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 SP_UL_DB_WATCH_H
+#define SP_UL_DB_WATCH_H
+
+#include "../../locking.h"
+#include "ul_db_handle.h"
+
+int ul_db_watch_init(void);
+
+void ul_db_watch_destroy(void);
+
+int init_db_check(void);
+
+int ul_register_watch_db(int id);
+
+int ul_unregister_watch_db(int id);
+
+#endif

+ 565 - 0
modules_k/p_usrloc/ul_mi.c

@@ -0,0 +1,565 @@
+/*
+ * $Id: ul_mi.c 5194 2008-11-13 10:38:11Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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
+ *
+ * History:
+ * ---------
+ *
+ * 2006-12-01  created (bogdan)
+ */
+
+/*! \file
+ *  \brief USRLOC - Usrloc MI functions
+ *  \ingroup usrloc
+ *
+ * - Module: \ref usrloc
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../qvalue.h"
+#include "../../ip_addr.h"
+#include "ul_mi.h"
+#include "dlist.h"
+#include "udomain.h"
+#include "utime.h"
+#include "ul_mod.h"
+
+
+/*! CSEQ nr used */
+#define MI_UL_CSEQ 1
+/*! call-id used for ul_add and ul_rm_contact */
+static str mi_ul_cid = str_init("[email protected]");
+/*! user agent used for ul_add */
+static str mi_ul_ua  = str_init("Kamailio MI Server");
+
+static str mi_ul_path = str_init("dummypath");
+
+/************************ helper functions ****************************/
+
+/*!
+ * \brief Search a domain in the global domain list
+ * \param table domain (table) name
+ * \return pointer to domain if found, 0 if not found
+ */
+static inline udomain_t* mi_find_domain(str* table)
+{
+
+	LM_ERR("not available in sp-ul_db mode");
+	return 0;
+}
+
+
+/*!
+ * \brief Convert address of record
+ *
+ * Convert an address of record string to lower case, and truncate
+ * it when use_domain is not set.
+ * \param aor address of record
+ * \return 0 on success, -1 on error
+ */
+static inline int mi_fix_aor(str *aor)
+{
+	char *p;
+
+	p = memchr( aor->s, '@', aor->len);
+	if (use_domain) {
+		if (p==NULL)
+			return -1;
+	} else {
+		if (p)
+			aor->len = p - aor->s;
+	}
+	strlower(aor);
+
+	return 0;
+}
+
+
+/*!
+ * \brief Add a node for a address of record
+ * \param parent parent node
+ * \param r printed record
+ * \param t actual time
+ * \param short_dump 0 means that all informations will be included, 1 that only the AOR is printed
+ * \return 0 on success, -1 on failure
+ */
+static inline int mi_add_aor_node(struct mi_node *parent, urecord_t* r, time_t t, int short_dump)
+{
+	struct mi_node *anode, *cnode, *node;
+	struct mi_attr *attr;
+	ucontact_t* c;
+	char *p;
+	int len;
+
+	anode = add_mi_node_child( parent, MI_DUP_VALUE, "AOR", 3,
+			r->aor.s, r->aor.len);
+	if (anode==0)
+		return -1;
+
+	if (short_dump)
+		return 0;
+
+	for( c=r->contacts ; c ; c=c->next) {
+		/* contact */
+		cnode = add_mi_node_child( anode, MI_DUP_VALUE, "Contact", 7,
+			c->c.s, c->c.len);
+		if (cnode==0)
+			return -1;
+
+		/* expires */
+		if (c->expires == 0) {
+			node = add_mi_node_child( cnode, 0, "Expires", 7, "permanent", 9);
+		} else if (c->expires == UL_EXPIRED_TIME) {
+			node = add_mi_node_child( cnode, 0, "Expires", 7, "deleted", 7);
+		} else if (t > c->expires) {
+			node = add_mi_node_child( cnode, 0, "Expires", 7, "expired", 7);
+		} else {
+			p = int2str((unsigned long)(c->expires - t), &len);
+			node = add_mi_node_child( cnode, MI_DUP_VALUE, "Expires", 7,p,len);
+		}
+		if (node==0)
+			return -1;
+
+		/* q */
+		p = q2str(c->q, (unsigned int*)&len);
+		attr = add_mi_attr( cnode, MI_DUP_VALUE, "Q", 1, p, len);
+		if (attr==0)
+			return -1;
+
+		/* callid */
+		node = add_mi_node_child( cnode, MI_DUP_VALUE, "Callid", 6,
+			c->callid.s, c->callid.len);
+		if (node==0)
+			return -1;
+
+		/* cseq */
+		p = int2str((unsigned long)c->cseq, &len);
+		node = add_mi_node_child( cnode, MI_DUP_VALUE, "Cseq", 4, p, len);
+		if (node==0)
+			return -1;
+
+		/* User-Agent */
+		if (c->user_agent.len) {
+			node = add_mi_node_child( cnode, MI_DUP_VALUE, "User-agent", 10,
+				c->user_agent.s, c->user_agent.len);
+			if (node==0)
+				return -1;
+		}
+
+		/* received */
+		if (c->received.len) {
+			node = add_mi_node_child( cnode, MI_DUP_VALUE, "Received", 8,
+				c->received.s, c->received.len);
+			if (node==0)
+				return -1;
+		}
+
+		/* path */
+		if (c->path.len) {
+			node = add_mi_node_child( cnode, MI_DUP_VALUE, "Path", 4,
+				c->path.s, c->path.len);
+			if (node==0)
+				return -1;
+		}
+
+		/* state */
+		if (c->state == CS_NEW) {
+			node = add_mi_node_child( cnode, 0, "State", 5, "CS_NEW", 6);
+		} else if (c->state == CS_SYNC) {
+			node = add_mi_node_child( cnode, 0, "State", 5, "CS_SYNC", 7);
+		} else if (c->state== CS_DIRTY) {
+			node = add_mi_node_child( cnode, 0, "State", 5, "CS_DIRTY", 8);
+		} else {
+			node = add_mi_node_child( cnode, 0, "State", 5, "CS_UNKNOWN", 10);
+		}
+		if (node==0)
+			return -1;
+
+		/* flags */
+		p = int2str((unsigned long)c->flags, &len);
+		node = add_mi_node_child( cnode, MI_DUP_VALUE, "Flags", 5, p, len);
+		if (node==0)
+			return -1;
+
+		/* cflags */
+		p = int2str((unsigned long)c->cflags, &len);
+		node = add_mi_node_child( cnode, MI_DUP_VALUE, "Cflags", 5, p, len);
+		if (node==0)
+			return -1;
+
+		/* socket */
+		if (c->sock) {
+			node = add_mi_node_child( cnode, 0, "Socket", 6,
+				c->sock->sock_str.s, c->sock->sock_str.len);
+			if (node==0)
+				return -1;
+		}
+
+		/* methods */
+		p = int2str((unsigned long)c->methods, &len);
+		node = add_mi_node_child( cnode, MI_DUP_VALUE, "Methods", 7, p, len);
+		if (node==0)
+			return -1;
+
+	} /* for */
+
+	return 0;
+}
+
+
+/*************************** MI functions *****************************/
+
+/*!
+ * \brief Delete a address of record including its contacts
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \note expects 2 nodes: the table name and the AOR
+ * \return mi_root with the result
+ */
+struct mi_root* mi_usrloc_rm_aor(struct mi_root *cmd, void *param)
+{
+	struct mi_node *node;
+	udomain_t *dom;
+	str *aor;
+
+	node = cmd->node.kids;
+	if (node==NULL || node->next==NULL || node->next->next!=NULL)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	/* look for table */
+	dom = mi_find_domain( &node->value );
+	if (dom==NULL)
+		return init_mi_tree( 404, "Table not found", 15);
+
+	/* process the aor */
+	aor = &node->next->value;
+	if ( mi_fix_aor(aor)!=0 )
+		return init_mi_tree( 400, "Domain missing in AOR", 21);
+
+	lock_udomain( dom, aor);
+	if (delete_urecord( dom, aor, 0) < 0) {
+		unlock_udomain( dom, aor);
+		return init_mi_tree( 500, "Failed to delete AOR", 20);
+	}
+
+	unlock_udomain( dom, aor);
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+}
+
+
+/*!
+ * \brief Delete a contact from an AOR record
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \note expects 3 nodes: the table name, the AOR and contact
+ * \return mi_root with the result or 0 on failure
+ */
+struct mi_root* mi_usrloc_rm_contact(struct mi_root *cmd, void *param)
+{
+	struct mi_node *node;
+	udomain_t *dom;
+	urecord_t *rec;
+	ucontact_t* con;
+	str *aor, *contact;
+	int ret;
+
+	node = cmd->node.kids;
+	if (node==NULL || node->next==NULL || node->next->next==NULL ||
+	node->next->next->next!=NULL)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	/* look for table */
+	dom = mi_find_domain( &node->value );
+	if (dom==NULL)
+		return init_mi_tree( 404, "Table not found", 15);
+
+	/* process the aor */
+	aor = &node->next->value;
+	if ( mi_fix_aor(aor)!=0 )
+		return init_mi_tree( 400, "Domain missing in AOR", 21);
+
+	lock_udomain( dom, aor);
+
+	ret = get_urecord( dom, aor, &rec);
+	if (ret == 1) {
+		unlock_udomain( dom, aor);
+		return init_mi_tree( 404, "AOR not found", 13);
+	}
+
+	contact = &node->next->next->value;
+	ret = get_ucontact( rec, contact, &mi_ul_cid, &mi_ul_path, MI_UL_CSEQ+1, &con);
+	if (ret < 0) {
+		unlock_udomain( dom, aor);
+		return 0;
+	}
+	if (ret > 0) {
+		unlock_udomain( dom, aor);
+		return init_mi_tree( 404, "Contact not found", 17);
+	}
+
+	if (delete_ucontact(rec, con) < 0) {
+		unlock_udomain( dom, aor);
+		return 0;
+	}
+
+	release_urecord(rec);
+	unlock_udomain( dom, aor);
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+}
+
+
+/*!
+ * \brief Dump the content of the usrloc
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \return mi_root with the result or 0 on failure
+ */
+struct mi_root* mi_usrloc_dump(struct mi_root *cmd, void *param)
+{
+	LM_ERR("not available in sp-ul_db mode");
+	return 0;
+}
+
+
+/*!
+ * \brief Flush the usrloc memory cache to DB
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \return mi_root with the result or 0 on failure
+ */
+struct mi_root* mi_usrloc_flush(struct mi_root *cmd, void *param)
+{
+	struct mi_root *rpl_tree;
+
+	rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+	if (rpl_tree==NULL)
+		return 0;
+
+	synchronize_all_udomains();
+	return rpl_tree;
+}
+
+
+/*!
+ * \brief Add a new contact for an address of record
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \note Expects 7 nodes: table name, AOR, contact, expires, Q,
+ * useless - backward compatible, flags, cflags, methods
+ * \return mi_root with the result
+ */
+struct mi_root* mi_usrloc_add(struct mi_root *cmd, void *param)
+{
+	ucontact_info_t ci;
+	urecord_t* r;
+	ucontact_t* c;
+	struct mi_node *node;
+	udomain_t *dom;
+	str *aor, *contact;
+	unsigned int ui_val;
+	int n;
+
+	for( n=0,node = cmd->node.kids; n<9 && node ; n++,node=node->next );
+	if (n!=9 || node!=0)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	node = cmd->node.kids;
+
+	/* look for table (param 1) */
+	dom = mi_find_domain( &node->value );
+	if (dom==NULL)
+		return init_mi_tree( 404, "Table not found", 15);
+
+	/* process the aor (param 2) */
+	node = node->next;
+	aor = &node->value;
+	if ( mi_fix_aor(aor)!=0 )
+		return init_mi_tree( 400, "Domain missing in AOR", 21);
+
+	/* contact (param 3) */
+	node = node->next;
+	contact = &node->value;
+
+	memset( &ci, 0, sizeof(ucontact_info_t));
+
+	/* expire (param 4) */
+	node = node->next;
+	if (str2int( &node->value, &ui_val) < 0)
+		goto bad_syntax;
+	ci.expires = ui_val;
+
+	/* q value (param 5) */
+	node = node->next;
+	if (str2q( &ci.q, node->value.s, node->value.len) < 0)
+		goto bad_syntax;
+
+	/* unused value (param 6) FIXME */
+	node = node->next;
+
+	/* flags value (param 7) */
+	node = node->next;
+	if (str2int( &node->value, (unsigned int*)&ci.flags) < 0)
+		goto bad_syntax;
+
+	/* branch flags value (param 8) */
+	node = node->next;
+	if (str2int( &node->value, (unsigned int*)&ci.cflags) < 0)
+		goto bad_syntax;
+
+	/* methods value (param 9) */
+	node = node->next;
+	if (str2int( &node->value, (unsigned int*)&ci.methods) < 0)
+		goto bad_syntax;
+
+	lock_udomain( dom, aor);
+
+	n = get_urecord( dom, aor, &r);
+	if ( n==1) {
+		if (insert_urecord( dom, aor, &r) < 0)
+			goto lock_error;
+		c = 0;
+	} else {
+		if (get_ucontact( r, contact, &mi_ul_cid, &mi_ul_path, MI_UL_CSEQ+1, &c) < 0)
+			goto lock_error;
+	}
+
+	get_act_time();
+
+	ci.callid = &mi_ul_cid;
+	ci.user_agent = &mi_ul_ua;
+	ci.cseq = MI_UL_CSEQ;
+	/* 0 expires means permanent contact */
+	if (ci.expires!=0)
+		ci.expires += act_time;
+
+	if (c) {
+		if (update_ucontact( r, c, &ci) < 0)
+			goto release_error;
+	} else {
+		if ( insert_ucontact( r, contact, &ci, &c) < 0 )
+			goto release_error;
+	}
+
+	release_urecord(r);
+
+	unlock_udomain( dom, aor);
+
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+bad_syntax:
+	return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+release_error:
+	release_urecord(r);
+lock_error:
+	unlock_udomain( dom, aor);
+	return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+}
+
+
+/*!
+ * \brief Dumps the contacts of an AOR
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \note expects 2 nodes: the table name and the AOR
+ * \return mi_root with the result or 0 on failure
+ */
+struct mi_root* mi_usrloc_show_contact(struct mi_root *cmd, void *param)
+{
+	struct mi_root *rpl_tree;
+	struct mi_node *rpl, *node;
+	udomain_t *dom;
+	urecord_t *rec;
+	ucontact_t* con;
+	str *aor;
+	int ret;
+
+	node = cmd->node.kids;
+	if (node==NULL || node->next==NULL || node->next->next!=NULL)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	/* look for table */
+	dom = mi_find_domain( &node->value );
+	if (dom==NULL)
+		return init_mi_tree( 404, "Table not found", 15);
+
+	/* process the aor */
+	aor = &node->next->value;
+	if ( mi_fix_aor(aor)!=0 )
+		return init_mi_tree( 400, "Domain missing in AOR", 21);
+
+	lock_udomain( dom, aor);
+
+	ret = get_urecord( dom, aor, &rec);
+	if (ret == 1) {
+		unlock_udomain( dom, aor);
+		return init_mi_tree( 404, "AOR not found", 13);
+	}
+
+	get_act_time();
+	rpl_tree = 0;
+	rpl = 0;
+
+	for( con=rec->contacts ; con ; con=con->next) {
+		if (VALID_CONTACT( con, act_time)) {
+			if (rpl_tree==0) {
+				rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+				if (rpl_tree==0)
+					goto error;
+				rpl = &rpl_tree->node;
+			}
+
+			node = addf_mi_node_child( rpl, 0, "Contact", 7,
+				"<%.*s>;q=%s;expires=%d;flags=0x%X;cflags=0x%X;socket=<%.*s>;"
+				"methods=0x%X"
+				"%s%.*s%s" /*received*/
+				"%s%.*s%s" /*user-agent*/
+				"%s%.*s%s", /*path*/
+				con->c.len, ZSW(con->c.s),
+				q2str(con->q, 0), (int)(con->expires - act_time),
+				con->flags, con->cflags,
+				con->sock?con->sock->sock_str.len:3,
+					con->sock?con->sock->sock_str.s:"NULL",
+				con->methods,
+				con->received.len?";received=<":"",con->received.len,
+					ZSW(con->received.s), con->received.len?">":"",
+				con->user_agent.len?";user_agent=<":"",con->user_agent.len,
+					ZSW(con->user_agent.s), con->user_agent.len?">":"",
+				con->path.len?";path=<":"", con->path.len,
+					ZSW(con->path.s), con->path.len?">":""
+				);
+			if (node==0)
+				goto error;
+		}
+	}
+
+	unlock_udomain( dom, aor);
+
+	if (rpl_tree==0)
+		return init_mi_tree( 404 , "AOR has no contacts", 18);
+
+	return rpl_tree;
+error:
+	if (rpl_tree)
+		free_mi_tree( rpl_tree );
+	unlock_udomain( dom, aor);
+	return 0;
+}

+ 101 - 0
modules_k/p_usrloc/ul_mi.h

@@ -0,0 +1,101 @@
+/*
+ * $Id: ul_mi.h 5194 2008-11-13 10:38:11Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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
+ */
+
+/*! \file
+ *  \brief USRLOC - Usrloc MI functions
+ *  \ingroup usrloc
+ */
+
+
+#ifndef _USRLOC_MI_H_
+#define _USRLOC_MI_H_
+
+#include "../../lib/kmi/mi.h"
+
+#define MI_USRLOC_RM           "ul_rm"
+#define MI_USRLOC_RM_CONTACT   "ul_rm_contact"
+#define MI_USRLOC_DUMP         "ul_dump"
+#define MI_USRLOC_FLUSH        "ul_flush"
+#define MI_USRLOC_ADD          "ul_add"
+#define MI_USRLOC_SHOW_CONTACT "ul_show_contact"
+
+
+/*!
+ * \brief Delete a address of record including its contacts
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \note expects 2 nodes: the table name and the AOR
+ * \return mi_root with the result
+ */
+struct mi_root* mi_usrloc_rm_aor(struct mi_root *cmd, void *param);
+
+
+/*!
+ * \brief Delete a contact from an AOR record
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \note expects 3 nodes: the table name, the AOR and contact
+ * \return mi_root with the result or 0 on failure
+ */
+struct mi_root* mi_usrloc_rm_contact(struct mi_root *cmd, void *param);
+
+
+/*!
+ * \brief Dump the content of the usrloc
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \return mi_root with the result or 0 on failure
+ */
+struct mi_root* mi_usrloc_dump(struct mi_root *cmd, void *param);
+
+
+/*!
+ * \brief Flush the usrloc memory cache to DB
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \return mi_root with the result or 0 on failure
+ */
+struct mi_root* mi_usrloc_flush(struct mi_root *cmd, void *param);
+
+
+/*!
+ * \brief Add a new contact for an address of record
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \note Expects 7 nodes: table name, AOR, contact, expires, Q,
+ * useless - backward compatible, flags, cflags, methods
+ * \return mi_root with the result
+ */
+struct mi_root* mi_usrloc_add(struct mi_root *cmd, void *param);
+
+
+/*!
+ * \brief Dumps the contacts of an AOR
+ * \param cmd mi_root containing the parameter
+ * \param param not used
+ * \note expects 2 nodes: the table name and the AOR
+ * \return mi_root with the result or 0 on failure
+ */
+struct mi_root* mi_usrloc_show_contact(struct mi_root *cmd, void *param);
+
+
+#endif

+ 541 - 0
modules_k/p_usrloc/ul_mod.c

@@ -0,0 +1,541 @@
+/*
+ * $Id$
+ *
+ * Usrloc module interface
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ---------
+ * 2003-01-27 timer activity printing #ifdef-ed to EXTRA_DEBUG (jiri)
+ * 2003-03-11 New module interface (janakj)
+ * 2003-03-12 added replication and state columns (nils)
+ * 2003-03-16 flags export parameter added (janakj)
+ * 2003-04-05: default_uri #define used (jiri)
+ * 2003-04-21 failed fifo init stops init process (jiri)
+ * 2004-03-17 generic callbacks added (bogdan)
+ * 2004-06-07 updated to the new DB api (andrei)
+ * 2011-01-03 extended module definition for partitioned userlocking (marius)
+ */
+
+/*! \file
+ *  \brief USRLOC - Usrloc module interface
+ *  \ingroup usrloc
+ *
+ * - Module \ref usrloc
+ */
+
+/*!
+ * \defgroup usrloc Usrloc :: User location module
+ * \brief User location module
+ *
+ * The module keeps a user location table and provides access
+ * to the table to other modules. The module exports no functions
+ * that could be used directly from scripts, all access is done
+ * over a API. A main user of this API is the registrar module.
+ * \see registrar
+ */
+
+#include <stdio.h>
+#include "ul_mod.h"
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../rpc_lookup.h"
+#include "../../timer.h"     /* register_timer */
+#include "../../globals.h"   /* is_main */
+#include "../../ut.h"        /* str_init */
+#include "dlist.h"           /* register_udomain */
+#include "udomain.h"         /* {insert,delete,get,release}_urecord */
+#include "urecord.h"         /* {insert,delete,get}_ucontact */
+#include "ucontact.h"        /* update_ucontact */
+#include "ul_mi.h"
+#include "ul_callback.h"
+#include "usrloc.h"
+#include "ul_db_api.h"
+#include "ul_db_watch.h"
+#include "ul_check.h"
+#include "ul_db.h"
+#include "ul_db_layer.h"
+
+MODULE_VERSION
+
+#define USER_COL       "username"
+#define DOMAIN_COL     "domain"
+#define CONTACT_COL    "contact"
+#define EXPIRES_COL    "expires"
+#define Q_COL          "q"
+#define CALLID_COL     "callid"
+#define CSEQ_COL       "cseq"
+#define FLAGS_COL      "flags"
+#define CFLAGS_COL 	"cflags"
+#define USER_AGENT_COL "user_agent"
+#define RECEIVED_COL   "received"
+#define PATH_COL       "path"
+#define SOCK_COL       "socket"
+#define METHODS_COL    "methods"
+#define LAST_MOD_COL   "last_modified"
+
+static int mod_init(void);                          /*!< Module initialization function */
+static void destroy(void);                          /*!< Module destroy function */
+static void timer(unsigned int ticks, void* param); /*!< Timer handler */
+static int child_init(int rank);                    /*!< Per-child init function */
+static int mi_child_init(void);
+static int mi_child_loc_nr_init(void);
+extern int bind_usrloc(usrloc_api_t* api);
+extern int ul_locks_no;
+/*
+ * Module parameters and their default values
+ */
+
+/**
+ * @var params
+ * defines the parameters which can be set in the openser config file
+ * @param first_column specifies the column in which the first key is stored
+ * @param second_column specifies the column in which the second key
+ * is stored. Only used when @see use_second_key is set to 1
+ * @param write_db_url Url to the database where the key and database information is 
+ * stored and where errors are reported to. Only used when @see write_on_db is active.
+ * @param read_db_url Url to the database where the key and database information is 
+ * stored.
+ * @param use_second_key specifies if the second key is used for database lookup
+ * identification. default is 1 (incactive)
+ * @param reg_db_table the name of the table containing the information about the 
+ * partitioned databases.
+ * @param id_column name of the column containing the id mapping to a key.
+ * @param num_column name of the column containing the number of the entry.
+ * @param url_column name of the column containing the URL to the database.
+ * @param status_column name of the column containing the status of the database. 
+ * (1=ON, 2=OFF)
+ * @param failover_time_column name of the column containing the time whem the 
+ * database's status changed or a spare has been activated.
+ * @param spare_flag_column name of the column containing the information if an entry
+ * works as spare for broken dbs (0=no spare, 1=spare)
+ * @param error_column name of the column containing the errors which occured on 
+ * the database.
+ * @param risk_group_column name of the column containing the databases risk group
+ * Only used when spare databases are used.
+ * @param expire_time specifies the expire time of contacts
+ * @param db_err_threshold specifies the amount of errors when at which a db 
+ * gets deactivated
+ * @param failover_level defines if the module shall search for spares or just
+ * turnoff a broken db
+ * @param db_retry_interval defines in which intervals the module shall try to 
+ * reconnect to a deactivated database
+ * @param write_on_db defines if the module has write access on the databases or not
+ * @param alg_location defines the algorithm for the location matching 0 - based on db_location field
+ * 1 - based on crc32
+ */
+
+str user_col        = str_init(USER_COL); 		/*!< Name of column containing usernames */
+str domain_col      = str_init(DOMAIN_COL); 		/*!< Name of column containing domains */
+str contact_col     = str_init(CONTACT_COL);		/*!< Name of column containing contact addresses */
+str expires_col     = str_init(EXPIRES_COL);		/*!< Name of column containing expires values */
+str q_col           = str_init(Q_COL);			/*!< Name of column containing q values */
+str callid_col      = str_init(CALLID_COL);		/*!< Name of column containing callid string */
+str cseq_col        = str_init(CSEQ_COL);		/*!< Name of column containing cseq values */
+str flags_col       = str_init(FLAGS_COL);		/*!< Name of column containing internal flags */
+str cflags_col       = str_init(CFLAGS_COL);
+str user_agent_col  = str_init(USER_AGENT_COL);		/*!< Name of column containing user agent string */
+str received_col    = str_init(RECEIVED_COL);		/*!< Name of column containing transport info of REGISTER */
+str path_col        = str_init(PATH_COL);		/*!< Name of column containing the Path header */
+str sock_col        = str_init(SOCK_COL);		/*!< Name of column containing the received socket */
+str methods_col     = str_init(METHODS_COL);		/*!< Name of column containing the supported methods */
+str last_mod_col     = str_init(LAST_MOD_COL);		/*!< Name of column containing the last modified date */
+str db_url          = str_init(DEFAULT_DB_URL);		/*!< Database URL */
+int timer_interval  = 60;				/*!< Timer interval in seconds */
+int db_mode         = 0;				/*!< Database sync scheme: 0-no db, 1-write through, 2-write back, 3-only db */
+int use_domain      = 0;				/*!< Whether usrloc should use domain part of aor */
+int desc_time_order = 0;				/*!< By default do not enable timestamp ordering */
+
+int ul_fetch_rows = 2000;				/*!< number of rows to fetch from result */
+int ul_hash_size = 9;
+str first_col            = {FIRST_COL, sizeof(FIRST_COL) - 1};
+str second_col           = {SECOND_COL, sizeof(SECOND_COL) - 1};
+str write_db_url         = {DEFAULT_DB_URL, DEFAULT_DB_URL_LEN};
+str read_db_url          = {DEFAULT_DB_URL, DEFAULT_DB_URL_LEN};
+int use_second_key       = 0;
+str reg_table            = {REG_TABLE, sizeof(REG_TABLE) -1};
+str subscr_table         = {SUBSCR_TABLE, sizeof(SUBSCR_TABLE) -1};
+str id_col               = {ID_COL, sizeof(ID_COL) - 1};
+str url_col              = {URL_COL, sizeof(URL_COL) - 1};
+str num_col              = {NUM_COL, sizeof(NUM_COL) - 1};
+str status_col           = {STATUS_COL, sizeof(STATUS_COL) - 1};
+str failover_time_col    = {FAILOVER_T_COL, sizeof(FAILOVER_T_COL) - 1};
+str spare_col            = {SPARE_COL, sizeof(SPARE_COL) - 1};
+str error_col            = {ERROR_COL, sizeof(ERROR_COL) - 1};
+str risk_group_col       = {RISK_GROUP_COL, sizeof(RISK_GROUP_COL) - 1};
+int expire_time          = DEFAULT_EXPIRE;
+int db_error_threshold   = DEFAULT_ERR_THRESHOLD;
+int failover_level       = DEFAULT_FAILOVER_LEVEL;
+int retry_interval       = DB_RETRY;
+int policy               = DB_DEFAULT_POLICY;
+int db_write             = 0;
+int db_master_write      = 0;
+int alg_location         = 0;
+
+int db_use_transactions  = 0;
+str db_transaction_level = {DB_DEFAULT_TRANSACTION_LEVEL, sizeof(DB_DEFAULT_TRANSACTION_LEVEL) -1};
+char * isolation_level;
+int connection_expires   = DB_DEFAULT_CONNECTION_EXPIRES;
+int max_loc_nr  = 0 ;
+
+
+/* flags */
+unsigned int nat_bflag = (unsigned int)-1;
+unsigned int init_flag = 0;
+
+str default_db_url    = str_init(DEFAULT_DB_URL);
+str default_db_type   = str_init(DEFAULT_DB_TYPE);
+str domain_db         = str_init(DEFAULT_DOMAIN_DB);
+int default_dbt       = 0;
+int expire            = 0;
+
+
+/*! \brief
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+	{"ul_bind_usrloc",        (cmd_function)bind_usrloc,        1, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0}
+};
+
+
+/*! \brief
+ * Exported parameters 
+ */
+static param_export_t params[] = {
+	{"user_column",       STR_PARAM, &user_col.s      },
+	{"domain_column",     STR_PARAM, &domain_col.s    },
+	{"contact_column",    STR_PARAM, &contact_col.s   },
+	{"expires_column",    STR_PARAM, &expires_col.s   },
+	{"q_column",          STR_PARAM, &q_col.s         },
+	{"callid_column",     STR_PARAM, &callid_col.s    },
+	{"cseq_column",       STR_PARAM, &cseq_col.s      },
+	{"flags_column",      STR_PARAM, &flags_col.s     },
+	{"cflags_column",     STR_PARAM, &cflags_col.s    },
+	{"db_url",            STR_PARAM, &db_url.s        },
+	{"timer_interval",    INT_PARAM, &timer_interval  },
+	{"db_mode",           INT_PARAM, &db_mode         },
+	{"use_domain",        INT_PARAM, &use_domain      },
+	{"desc_time_order",   INT_PARAM, &desc_time_order },
+	{"user_agent_column", STR_PARAM, &user_agent_col.s},
+	{"received_column",   STR_PARAM, &received_col.s  },
+	{"path_column",       STR_PARAM, &path_col.s      },
+	{"socket_column",     STR_PARAM, &sock_col.s      },
+	{"methods_column",    STR_PARAM, &methods_col.s   },
+	{"matching_mode",     INT_PARAM, &matching_mode   },
+	{"cseq_delay",        INT_PARAM, &cseq_delay      },
+	{"fetch_rows",        INT_PARAM, &ul_fetch_rows   },
+	{"hash_size",         INT_PARAM, &ul_hash_size    },
+	{"nat_bflag",         INT_PARAM, &nat_bflag       },
+	{"default_db_url",    STR_PARAM, &default_db_url.s    },
+	{"default_db_type",   STR_PARAM, &default_db_type.s   },
+	{"domain_db",         STR_PARAM, &domain_db.s         },
+	{"first_column",         STR_PARAM, &first_col.s          },
+	{"second_column",        STR_PARAM, &second_col.s        },
+	{"write_db_url",         STR_PARAM, &write_db_url.s      },
+	{"read_db_url",          STR_PARAM, &read_db_url.s       },
+	{"use_second_key",       INT_PARAM, &use_second_key      },
+	{"reg_db_table",         STR_PARAM, &reg_table.s         },
+	{"id_column",            STR_PARAM, &id_col.s            },
+	{"num_column",           STR_PARAM, &num_col.s           },
+	{"url_column",           STR_PARAM, &url_col.s           },
+	{"status_column",        STR_PARAM, &status_col.s        },
+	{"failover_time_column", STR_PARAM, &failover_time_col.s },
+	{"spare_flag_column",    STR_PARAM, &spare_col.s         },
+	{"error_column",         STR_PARAM, &error_col.s         },
+	{"risk_group_column",    STR_PARAM, &risk_group_col.s    },
+	{"expire_time",          INT_PARAM, &expire_time         },
+	{"db_err_threshold",     INT_PARAM, &db_error_threshold  },
+	{"failover_level",       INT_PARAM, &failover_level      },
+	{"db_retry_interval",    INT_PARAM, &retry_interval      },
+	{"db_use_transactions",  INT_PARAM, &db_use_transactions },
+	{"db_transaction_level", INT_PARAM, &db_transaction_level},
+	{"write_on_db",          INT_PARAM, &db_write            },
+	{"write_on_master_db",   INT_PARAM, &db_master_write     },
+	{"connection_expires",   INT_PARAM, &connection_expires  },
+	{"alg_location",         INT_PARAM, &alg_location },
+	{0, 0, 0}
+};
+
+
+stat_export_t mod_stats[] = {
+	{"registered_users" ,  STAT_IS_FUNC, (stat_var**)get_number_of_users  },
+	{0,0,0}
+};
+
+
+static mi_export_t mi_cmds[] = {
+	{ MI_USRLOC_RM,           mi_usrloc_rm_aor,       0,                 0,
+				mi_child_init },
+	{ MI_USRLOC_RM_CONTACT,   mi_usrloc_rm_contact,   0,                 0,
+				mi_child_init },
+	{ MI_USRLOC_DUMP,         mi_usrloc_dump,         0,                 0,
+				0             },
+	{ MI_USRLOC_FLUSH,        mi_usrloc_flush,        MI_NO_INPUT_FLAG,  0,
+				mi_child_init },
+	{ MI_USRLOC_ADD,          mi_usrloc_add,          0,                 0,
+				mi_child_init },
+	{ MI_USRLOC_SHOW_CONTACT, mi_usrloc_show_contact, 0,                 0,
+				mi_child_init },
+	{ "loc_refresh", mi_ul_db_refresh, MI_NO_INPUT_FLAG,  0, mi_child_init },
+	{ "loc_nr_refresh", mi_loc_nr_refresh, MI_NO_INPUT_FLAG,  0, mi_child_loc_nr_init },
+	{ 0, 0, 0, 0, 0}
+};
+
+
+struct module_exports exports = {
+	"p_usrloc",
+	DEFAULT_DLFLAGS, /*!< dlopen flags */
+	cmds,       /*!< Exported functions */
+	params,     /*!< Export parameters */
+	mod_stats,  /*!< exported statistics */
+	mi_cmds,    /*!< exported MI functions */
+	0,          /*!< exported pseudo-variables */
+	0,          /*!< extra processes */
+	mod_init,   /*!< Module initialization function */
+	0,          /*!< Response function */
+	destroy,    /*!< Destroy function */
+	child_init  /*!< Child initialization function */
+};
+
+
+/*! \brief
+ * Module initialization function
+ */
+static int mod_init(void)
+{
+#ifdef STATISTICS
+	/* register statistics */
+	if (register_module_stats( exports.name, mod_stats)!=0 ) {
+		LM_ERR("failed to register core statistics\n");
+		return -1;
+	}
+#endif
+	
+	if(register_mi_mod(exports.name, mi_cmds)!=0)
+	{
+		LM_ERR("failed to register MI commands\n");
+		return -1;
+	}
+
+
+	/* Compute the lengths of string parameters */
+	user_col.len = strlen(user_col.s);
+	domain_col.len = strlen(domain_col.s);
+	contact_col.len = strlen(contact_col.s);
+	expires_col.len = strlen(expires_col.s);
+	q_col.len = strlen(q_col.s);
+	callid_col.len = strlen(callid_col.s);
+	cseq_col.len = strlen(cseq_col.s);
+	flags_col.len = strlen(flags_col.s);
+	cflags_col.len = strlen(cflags_col.s);
+	user_agent_col.len = strlen(user_agent_col.s);
+	received_col.len = strlen(received_col.s);
+	path_col.len = strlen(path_col.s);
+	sock_col.len = strlen(sock_col.s);
+	methods_col.len = strlen(methods_col.s);
+	last_mod_col.len = strlen(last_mod_col.s);
+	db_url.len = strlen(db_url.s);
+	
+	first_col.len = strlen (first_col.s);
+	second_col.len = strlen (second_col.s);
+	write_db_url.len = strlen (write_db_url.s);
+	read_db_url.len = strlen (read_db_url.s);
+	reg_table.len = strlen(reg_table.s);
+	id_col.len = strlen(id_col.s);
+	url_col.len = strlen(url_col.s);
+	num_col.len = strlen(num_col.s);
+	status_col.len = strlen(status_col.s);
+	failover_time_col.len = strlen(failover_time_col.s);
+	spare_col.len = strlen(spare_col.s);
+	error_col.len = strlen(error_col.s);
+	risk_group_col.len = strlen(risk_group_col.s);
+	db_transaction_level.len = strlen(db_transaction_level.s);
+
+	
+	if(ul_hash_size<=1)
+		ul_hash_size = 512;
+	else
+		ul_hash_size = 1<<ul_hash_size;
+	ul_locks_no = ul_hash_size;
+
+	/* check matching mode */
+	switch (matching_mode) {
+		case CONTACT_ONLY:
+		case CONTACT_CALLID:
+			break;
+		default:
+			LM_ERR("invalid matching mode %d\n", matching_mode);
+	}
+
+	if(ul_init_locks()!=0)
+	{
+		LM_ERR("locks array initialization failed\n");
+		return -1;
+	}
+
+	/* Register cache timer */
+	register_timer( timer, 0, timer_interval);
+
+	/* init the callbacks list */
+	if ( init_ulcb_list() < 0) {
+		LM_ERR("usrloc/callbacks initialization failed\n");
+		return -1;
+	}
+
+	/* Shall we use database ? */
+	if (db_mode != NO_DB) { /* Yes */
+		if(!default_db_url.s || !strlen(default_db_url.s)){
+			LM_ERR("must set default_db_url parameter\n");
+			return -1;
+		}
+		default_db_url.len = strlen (default_db_url.s);
+
+		domain_db.len = strlen(domain_db.s);
+		default_db_type.len = strlen(default_db_type.s);
+		if(strcmp(DB_TYPE_CLUSTER_STR, default_db_type.s) == 0){
+			default_dbt = DB_TYPE_CLUSTER;
+		} else {
+			default_dbt = DB_TYPE_SINGLE;
+		}
+
+		if (ul_db_layer_init() < 0) {
+			return -1;
+		}
+
+		if(ul_fetch_rows<=0) {
+			LM_ERR("invalid fetch_rows number '%d'\n", ul_fetch_rows);
+			return -1;
+		}
+	}
+
+	if (nat_bflag==(unsigned int)-1) {
+		nat_bflag = 0;
+	} else if ( nat_bflag>=8*sizeof(nat_bflag) ) {
+		LM_ERR("bflag index (%d) too big!\n", nat_bflag);
+		return -1;
+	} else {
+		nat_bflag = 1<<nat_bflag;
+	}
+
+	init_flag = 1;
+	
+	if((isolation_level = pkg_malloc(strlen("SET TRANSACTION ISOLATION LEVEL ") + db_transaction_level.len + 1)) == NULL){
+		LM_ERR("couldn't allocate private memory.\n");
+		return -1;
+	}
+	sprintf(isolation_level, "SET TRANSACTION ISOLATION LEVEL %s", db_transaction_level.s);
+	if(init_list() < 0) {
+		LM_ERR("could not init check list.\n");
+		return -1;
+	}
+	if(ul_db_init() < 0){
+		LM_ERR("could not initialise databases.\n");
+		return -1;
+	}
+	if(ul_db_watch_init() < 0){
+		LM_ERR("could not init database watch environment.\n");
+		return -1;
+	}
+	if(init_db_check() < 0){
+ 			LM_ERR("could not initialise database check.\n");
+ 			return -1;
+ 	}
+	return 0;
+}
+
+
+static int child_init(int _rank)
+{
+	if(ul_db_child_init() < 0){
+		LM_ERR("could not initialise databases.\n");
+		return -1;
+	}
+	
+
+	return 0;
+}
+
+
+/* */
+static int mi_child_init(void)
+{
+
+	return ul_db_child_init();
+}
+
+
+/*! \brief
+ * Module destroy function
+ */
+static void destroy(void)
+{
+	/* we need to sync DB in order to flush the cache */
+	ul_unlock_locks();
+	if (synchronize_all_udomains() != 0) {
+		LM_ERR("flushing cache failed\n");
+	}
+
+	free_all_udomains();
+	ul_destroy_locks();
+	/* free callbacks list */
+	destroy_ulcb_list();
+	
+	ul_db_shutdown();
+	destroy_list();
+	ul_db_watch_destroy();
+	
+}
+
+
+/*! \brief
+ * Timer handler
+ */
+static void timer(unsigned int ticks, void* param)
+{
+	if (synchronize_all_udomains() != 0) {
+		LM_ERR("synchronizing cache failed\n");
+	}
+}
+
+static int mi_child_loc_nr_init(void)
+{
+	if(ul_db_child_locnr_init() < 0){
+		LM_ERR("could not retrive location number from database. Try to reinitialize the db handles\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+struct mi_root*  mi_ul_db_refresh(struct mi_root* cmd_tree, void* param) {
+	int ret;
+	ret = set_must_refresh();
+	
+	LM_INFO("sp-ul_db location databases were refreshed (%i elements).\n", ret);
+
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+}
+
+
+struct mi_root*  mi_loc_nr_refresh(struct mi_root* cmd_tree, void* param) {
+	/* this function does nothing, all work is done per each child in the mi_child_loc_nr_init function */
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+}

+ 150 - 0
modules_k/p_usrloc/ul_mod.h

@@ -0,0 +1,150 @@
+/*
+ * $Id$
+ *
+ * User location module interface
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ---------
+ */
+
+/*! \file
+ *  \brief P_USRLOC - P-Usrloc module interface
+ *  \ingroup usrloc
+ */
+
+#ifndef UL_MOD_H
+#define UL_MOD_H
+
+
+#include "../../lib/srdb1/db.h"
+#include "../../str.h"
+#include "../../lib/kmi/mi.h"
+
+/*
+ * Module parameters
+ */
+
+
+#define NO_DB         0
+#define WRITE_THROUGH 1
+#define WRITE_BACK    2
+#define DB_ONLY       3
+
+#define UL_TABLE_VERSION 1004
+
+/*
+ * Matching algorithms
+ */
+#define CONTACT_ONLY            (0)
+#define CONTACT_CALLID          (1)
+#define CONTACT_PATH		(2)
+
+#define FIRST_COL      "username"
+#define SECOND_COL     "domain"
+#define REG_TABLE   "locdb"
+#define SUBSCR_TABLE   "subscriber"
+#define URL_COL        "url"
+#define ID_COL         "id"
+#define NUM_COL        "no"
+#define STATUS_COL      "status"
+#define FAILOVER_T_COL "failover"
+#define SPARE_COL      "spare"
+#define ERROR_COL      "errors"
+#define RISK_GROUP_COL "rg"
+#define DEFAULT_EXPIRE 3600
+#define DEFAULT_ERR_THRESHOLD 50
+#define DB_RETRY 10
+#define DB_DEFAULT_POLICY 0
+#define DEFAULT_FAILOVER_LEVEL 1
+#define DB_DEFAULT_TRANSACTION_LEVEL "READ UNCOMMITED"
+#define DB_DEFAULT_CONNECTION_EXPIRES 300
+#define DEFAULT_DB_TYPE "single"
+#define DEFAULT_DOMAIN_DB "location=cluster,cfa=single,cfb=single,cfnr=single,cfnl=single,cfu=single"
+
+extern str user_col;
+extern str domain_col;
+extern str contact_col;
+extern str expires_col;
+extern str q_col;
+extern str callid_col;
+extern str cseq_col;
+extern str flags_col;
+extern str cflags_col;
+extern str user_agent_col;
+extern str received_col;
+extern str path_col;
+extern str sock_col;
+extern str methods_col;
+extern str last_mod_col;
+
+extern str db_url;
+extern int timer_interval;
+extern int db_mode;
+extern int use_domain;
+extern int desc_time_order;
+extern int cseq_delay;
+extern int ul_fetch_rows;
+extern int ul_hash_size;
+
+
+
+extern str default_db_url;
+extern str default_db_type;
+extern int default_dbt;
+extern str domain_db;
+extern int expire;
+
+extern int matching_mode;
+
+struct mi_root* mi_ul_db_refresh(struct mi_root* cmd, void* param);
+struct mi_root* mi_loc_nr_refresh(struct mi_root* cmd, void* param);
+
+extern str first_col;
+extern str second_col;
+extern str write_db_url;
+extern str read_db_url;
+extern int use_second_key;
+extern str reg_table;
+extern str subscr_table;
+extern str id_col;
+extern str url_col;
+extern str num_col;
+extern str status_col;
+extern str failover_time_col;
+extern str spare_col;
+extern str error_col;
+extern str risk_group_col;
+extern int expire_time;
+extern int db_error_threshold;
+extern int failover_level;
+extern int retry_interval;
+extern int policy;
+extern int db_write;
+extern int db_master_write;
+extern int db_use_transactions;
+extern str db_transaction_level;
+extern char * isolation_level;
+extern int connection_expires;
+extern int alg_location;
+
+extern int  max_loc_nr;
+
+#endif /* UL_MOD_H */

+ 629 - 0
modules_k/p_usrloc/urecord.c

@@ -0,0 +1,629 @@
+/*
+ * $Id: urecord.c 5241 2008-11-21 12:52:25Z henningw $ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ---------
+ * 2003-03-12 added replication mark and zombie state support (nils)
+ * 2004-03-17 generic callbacks added (bogdan)
+ * 2004-06-07 updated to the new DB api (andrei)
+ */
+
+/*! \file
+ *  \brief USRLOC - Usrloc record structure
+ *  \ingroup usrloc
+ *
+ * - Module \ref usrloc
+ */
+
+
+#include "urecord.h"
+#include <string.h>
+#include "../../mem/shm_mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../lib/kcore/hash_func.h"
+#include "ul_mod.h"
+#include "utime.h"
+#include "ul_callback.h"
+#include "ul_db_layer.h"
+
+
+/*! contact matching mode */
+int matching_mode = CONTACT_ONLY;
+/*! retransmission detection interval in seconds */
+int cseq_delay = 20;
+
+/*!
+ * \brief Create and initialize new record structure
+ * \param _dom domain name
+ * \param _aor address of record
+ * \param _r pointer to the new record
+ * \return 0 on success, negative on failure
+ */
+int new_urecord(str* _dom, str* _aor, urecord_t** _r)
+{
+	*_r = (urecord_t*)shm_malloc(sizeof(urecord_t));
+	if (*_r == 0) {
+		LM_ERR("no more share memory\n");
+		return -1;
+	}
+	memset(*_r, 0, sizeof(urecord_t));
+
+	(*_r)->aor.s = (char*)shm_malloc(_aor->len);
+	if ((*_r)->aor.s == 0) {
+		LM_ERR("no more share memory\n");
+		shm_free(*_r);
+		*_r = 0;
+		return -2;
+	}
+	memcpy((*_r)->aor.s, _aor->s, _aor->len);
+	(*_r)->aor.len = _aor->len;
+	(*_r)->domain = _dom;
+	(*_r)->aorhash = core_hash(_aor, 0, 0);
+	return 0;
+}
+
+
+/*!
+ * \brief Free all memory used by the given structure
+ *
+ * Free all memory used by the given structure.
+ * The structure must be removed from all linked
+ * lists first
+ * \param _r freed record list
+ */
+void free_urecord(urecord_t* _r)
+{
+	ucontact_t* ptr;
+
+	while(_r->contacts) {
+		ptr = _r->contacts;
+		_r->contacts = _r->contacts->next;
+		free_ucontact(ptr);
+	}
+	
+	/* if mem cache is not used, the urecord struct is static*/
+	if (db_mode!=DB_ONLY) {
+		if (_r->aor.s) shm_free(_r->aor.s);
+		shm_free(_r);
+	}
+}
+
+
+/*!
+ * \brief Print a record, useful for debugging
+ * \param _f print output
+ * \param _r printed record
+ */
+void print_urecord(FILE* _f, urecord_t* _r)
+{
+	ucontact_t* ptr;
+
+	fprintf(_f, "...Record(%p)...\n", _r);
+	fprintf(_f, "domain : '%.*s'\n", _r->domain->len, ZSW(_r->domain->s));
+	fprintf(_f, "aor    : '%.*s'\n", _r->aor.len, ZSW(_r->aor.s));
+	fprintf(_f, "aorhash: '%u'\n", (unsigned)_r->aorhash);
+	fprintf(_f, "slot:    '%d'\n", _r->aorhash&(_r->slot->d->size-1));
+	
+	if (_r->contacts) {
+		ptr = _r->contacts;
+		while(ptr) {
+			print_ucontact(_f, ptr);
+			ptr = ptr->next;
+		}
+	}
+
+	fprintf(_f, ".../Record...\n");
+}
+
+
+/*!
+ * \brief Add a new contact in memory
+ *
+ * Add a new contact in memory, contacts are ordered by:
+ * 1) q value, 2) descending modification time
+ * \param _r record this contact belongs to
+ * \param _c contact
+ * \param _ci contact information
+ * \return pointer to new created contact on success, 0 on failure
+ */
+ucontact_t* mem_insert_ucontact(urecord_t* _r, str* _c, ucontact_info_t* _ci)
+{
+	ucontact_t* ptr, *prev = 0;
+	ucontact_t* c;
+
+	if ( (c=new_ucontact(_r->domain, &_r->aor, _c, _ci)) == 0) {
+		LM_ERR("failed to create new contact\n");
+		return 0;
+	}
+	if_update_stat( _r->slot, _r->slot->d->contacts, 1);
+
+	ptr = _r->contacts;
+
+	if (!desc_time_order) {
+		while(ptr) {
+			if (ptr->q < c->q) break;
+			prev = ptr;
+			ptr = ptr->next;
+		}
+	}
+
+	if (ptr) {
+		if (!ptr->prev) {
+			ptr->prev = c;
+			c->next = ptr;
+			_r->contacts = c;
+		} else {
+			c->next = ptr;
+			c->prev = ptr->prev;
+			ptr->prev->next = c;
+			ptr->prev = c;
+		}
+	} else if (prev) {
+		prev->next = c;
+		c->prev = prev;
+	} else {
+		_r->contacts = c;
+	}
+
+	return c;
+}
+
+
+/*!
+ * \brief Remove the contact from lists in memory
+ * \param _r record this contact belongs to
+ * \param _c removed contact
+ */
+void mem_remove_ucontact(urecord_t* _r, ucontact_t* _c)
+{
+	if (_c->prev) {
+		_c->prev->next = _c->next;
+		if (_c->next) {
+			_c->next->prev = _c->prev;
+		}
+	} else {
+		_r->contacts = _c->next;
+		if (_c->next) {
+			_c->next->prev = 0;
+		}
+	}
+}	
+
+
+/*!
+ * \brief Remove contact in memory from the list and delete it
+ * \param _r record this contact belongs to
+ * \param _c deleted contact
+ */
+void mem_delete_ucontact(urecord_t* _r, ucontact_t* _c)
+{
+	mem_remove_ucontact(_r, _c);
+	if_update_stat( _r->slot, _r->slot->d->contacts, -1);
+	free_ucontact(_c);
+}
+
+
+/*!
+ * \brief Expires timer for NO_DB db_mode
+ *
+ * Expires timer for NO_DB db_mode, process all contacts from
+ * the record, delete the expired ones from memory.
+ * \param _r processed record
+ */
+static inline void nodb_timer(urecord_t* _r)
+{
+	ucontact_t* ptr, *t;
+
+	ptr = _r->contacts;
+
+	while(ptr) {
+		if (!VALID_CONTACT(ptr, act_time)) {
+			/* run callbacks for EXPIRE event */
+			if (exists_ulcb_type(UL_CONTACT_EXPIRE))
+				run_ul_callbacks( UL_CONTACT_EXPIRE, ptr);
+
+			LM_DBG("Binding '%.*s','%.*s' has expired\n",
+				ptr->aor->len, ZSW(ptr->aor->s),
+				ptr->c.len, ZSW(ptr->c.s));
+
+			t = ptr;
+			ptr = ptr->next;
+
+			mem_delete_ucontact(_r, t);
+			update_stat( _r->slot->d->expires, 1);
+		} else {
+			ptr = ptr->next;
+		}
+	}
+}
+
+
+/*!
+ * \brief Write through timer, used for WRITE_THROUGH db_mode
+ *
+ * Write through timer, used for WRITE_THROUGH db_mode. Process all
+ * contacts from the record, delete all expired ones from the DB.
+ * \param _r processed record
+ * \note currently unused, this mode is also handled by the wb_timer
+ */
+static inline void wt_timer(urecord_t* _r)
+{
+	ucontact_t* ptr, *t;
+
+	ptr = _r->contacts;
+
+	while(ptr) {
+		if (!VALID_CONTACT(ptr, act_time)) {
+			/* run callbacks for EXPIRE event */
+			if (exists_ulcb_type(UL_CONTACT_EXPIRE)) {
+				run_ul_callbacks( UL_CONTACT_EXPIRE, ptr);
+			}
+
+			LM_DBG("Binding '%.*s','%.*s' has expired\n",
+				ptr->aor->len, ZSW(ptr->aor->s),
+				ptr->c.len, ZSW(ptr->c.s));
+
+			t = ptr;
+			ptr = ptr->next;
+
+			if (db_delete_ucontact(t) < 0) {
+				LM_ERR("deleting contact from database failed\n");
+			}
+			mem_delete_ucontact(_r, t);
+			update_stat( _r->slot->d->expires, 1);
+		} else {
+			ptr = ptr->next;
+		}
+	}
+}
+
+
+/*!
+ * \brief Write-back timer, used for WRITE_BACK db_mode
+ *
+ * Write-back timer, used for WRITE_BACK db_mode. Process
+ * all contacts from the record, delete expired ones from the DB.
+ * Furthermore it updates changed contacts, and also insert new
+ * ones in the DB.
+ * \param _r processed record
+ */
+static inline void wb_timer(urecord_t* _r)
+{
+	ucontact_t* ptr, *t;
+	cstate_t old_state;
+	int op;
+
+	ptr = _r->contacts;
+
+	while(ptr) {
+		if (!VALID_CONTACT(ptr, act_time)) {
+			/* run callbacks for EXPIRE event */
+			if (exists_ulcb_type(UL_CONTACT_EXPIRE)) {
+				run_ul_callbacks( UL_CONTACT_EXPIRE, ptr);
+			}
+
+			LM_DBG("Binding '%.*s','%.*s' has expired\n",
+				ptr->aor->len, ZSW(ptr->aor->s),
+				ptr->c.len, ZSW(ptr->c.s));
+			update_stat( _r->slot->d->expires, 1);
+
+			t = ptr;
+			ptr = ptr->next;
+
+			/* Should we remove the contact from the database ? */
+			if (st_expired_ucontact(t) == 1) {
+				if (db_delete_ucontact(t) < 0) {
+					LM_ERR("failed to delete contact from the database\n");
+				}
+			}
+
+			mem_delete_ucontact(_r, t);
+		} else {
+			/* Determine the operation we have to do */
+			old_state = ptr->state;
+			op = st_flush_ucontact(ptr);
+
+			switch(op) {
+			case 0: /* do nothing, contact is synchronized */
+				break;
+
+			case 1: /* insert */
+				if (db_insert_ucontact(ptr) < 0) {
+					LM_ERR("inserting contact into database failed\n");
+					ptr->state = old_state;
+				}
+				break;
+
+			case 2: /* update */
+				if (db_update_ucontact(ptr) < 0) {
+					LM_ERR("updating contact in db failed\n");
+					ptr->state = old_state;
+				}
+				break;
+			}
+
+			ptr = ptr->next;
+		}
+	}
+}
+
+
+/*!
+ * \brief Run timer functions depending on the db_mode setting.
+ *
+ * Helper function that run the appropriate timer function, depending
+ * on the db_mode setting.
+ * \param _r processed record
+ */
+void timer_urecord(urecord_t* _r)
+{
+	switch(db_mode) {
+	case NO_DB:         nodb_timer(_r);
+						break;
+	/* use also the write_back timer routine to handle the failed
+	 * realtime inserts/updates */
+	case WRITE_THROUGH: wb_timer(_r); /*wt_timer(_r);*/
+						break;
+	case WRITE_BACK:    wb_timer(_r);
+						break;
+	}
+}
+
+
+/*!
+ * \brief Delete a record from the database
+ * \param _r deleted record
+ * \return 0 on success, -1 on failure
+ */
+int db_delete_urecord(udomain_t* _d, urecord_t* _r)
+{
+	db_key_t keys[2];
+	db_val_t vals[2];
+	char* dom;
+
+	keys[0] = &user_col;
+	keys[1] = &domain_col;
+	vals[0].type = DB1_STR;
+	vals[0].nul = 0;
+	vals[0].val.str_val.s = _r->aor.s;
+	vals[0].val.str_val.len = _r->aor.len;
+
+	if (use_domain) {
+		dom = memchr(_r->aor.s, '@', _r->aor.len);
+		vals[0].val.str_val.len = dom - _r->aor.s;
+
+		vals[1].type = DB1_STR;
+		vals[1].nul = 0;
+		vals[1].val.str_val.s = dom + 1;
+		vals[1].val.str_val.len = _r->aor.s + _r->aor.len - dom - 1;
+	}
+
+	if (ul_db_layer_delete(_d, &vals[0].val.str_val, &vals[1].val.str_val, keys, 0, vals, (use_domain) ? (2) : (1)) < 0) {
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*!
+ * \brief Release urecord previously obtained through get_urecord
+ * \warning Failing to calls this function after get_urecord will
+ * result in a memory leak when the DB_ONLY mode is used. When
+ * the records is later deleted, e.g. with delete_urecord, then
+ * its not necessary, as this function already releases the record.
+ * \param _r released record
+ */
+void release_urecord(urecord_t* _r)
+{
+	if (db_mode==DB_ONLY) {
+		free_urecord(_r);
+	} else if (_r->contacts == 0) {
+		mem_delete_urecord(_r->slot->d, _r);
+	}
+}
+
+
+/*!
+ * \brief Create and insert new contact into urecord
+ * \param _r record into the new contact should be inserted
+ * \param _contact contact string
+ * \param _ci contact information
+ * \param _c new created contact
+ * \return 0 on success, -1 on failure
+ */
+int insert_ucontact(urecord_t* _r, str* _contact, ucontact_info_t* _ci,
+															ucontact_t** _c)
+{
+	if ( ((*_c)=mem_insert_ucontact(_r, _contact, _ci)) == 0) {
+		LM_ERR("failed to insert contact\n");
+		return -1;
+	}
+
+	if (exists_ulcb_type(UL_CONTACT_INSERT)) {
+		run_ul_callbacks( UL_CONTACT_INSERT, *_c);
+	}
+
+	if (db_mode == WRITE_THROUGH || db_mode==DB_ONLY) {
+		if (db_insert_ucontact(*_c) < 0) {
+			LM_ERR("failed to insert in database\n");
+			return -1;
+		} else {
+			(*_c)->state = CS_SYNC;
+		}
+	}
+
+	return 0;
+}
+
+
+/*!
+ * \brief Delete ucontact from urecord
+ * \param _r record where the contact belongs to
+ * \param _c deleted contact
+ * \return 0 on success, -1 on failure
+ */
+int delete_ucontact(urecord_t* _r, struct ucontact* _c)
+{
+	int ret = 0;
+
+	if (exists_ulcb_type(UL_CONTACT_DELETE)) {
+		run_ul_callbacks( UL_CONTACT_DELETE, _c);
+	}
+
+	if (st_delete_ucontact(_c) > 0) {
+		if (db_mode == WRITE_THROUGH || db_mode==DB_ONLY) {
+			if (db_delete_ucontact(_c) < 0) {
+				LM_ERR("failed to remove contact from database\n");
+				ret = -1;
+			}
+		}
+
+		mem_delete_ucontact(_r, _c);
+	}
+
+	return ret;
+}
+
+
+/*!
+ * \brief Match a contact record to a contact string
+ * \param ptr contact record
+ * \param _c contact string
+ * \return ptr on successfull match, 0 when they not match
+ */
+static inline struct ucontact* contact_match( ucontact_t* ptr, str* _c)
+{
+	while(ptr) {
+		if ((_c->len == ptr->c.len) && !memcmp(_c->s, ptr->c.s, _c->len)) {
+			return ptr;
+		}
+		
+		ptr = ptr->next;
+	}
+	return 0;
+}
+
+
+/*!
+ * \brief Match a contact record to a contact string and callid
+ * \param ptr contact record
+ * \param _c contact string
+ * \param _callid callid
+ * \return ptr on successfull match, 0 when they not match
+ */
+static inline struct ucontact* contact_callid_match( ucontact_t* ptr,
+														str* _c, str *_callid)
+{
+	while(ptr) {
+		if ( (_c->len==ptr->c.len) && (_callid->len==ptr->callid.len)
+		&& !memcmp(_c->s, ptr->c.s, _c->len)
+		&& !memcmp(_callid->s, ptr->callid.s, _callid->len)
+		) {
+			return ptr;
+		}
+		
+		ptr = ptr->next;
+	}
+	return 0;
+}
+
+
+ /*!
++ * \brief Match a contact record to a contact string and path
++ * \param ptr contact record
++ * \param _c contact string
++ * \param _path path
++ * \return ptr on successfull match, 0 when they not match
++ */
+static inline struct ucontact* contact_path_match( ucontact_t* ptr, str* _c, str *_path)
+{
+	/* if no path is preset (in REGISTER request) or use_path is not configured
+	   in registrar module, default to contact_match() */
+	if( _path == NULL) return contact_match(ptr, _c);
+
+	while(ptr) {
+		if ( (_c->len==ptr->c.len) && (_path->len==ptr->path.len)
+		&& !memcmp(_c->s, ptr->c.s, _c->len)
+		&& !memcmp(_path->s, ptr->path.s, _path->len)
+		) {
+			return ptr;
+		}
+
+		ptr = ptr->next;
+	}
+	return 0;
+}
+
+/*!
+ * \brief Get pointer to ucontact with given contact
+ * \param _r record where to search the contacts
+ * \param _c contact string
+ * \param _callid callid
+ * \param _cseq CSEQ number
+ * \param _co found contact
+ * \return 0 - found, 1 - not found, -1 - invalid found, 
+ * -2 - found, but to be skipped (same cseq)
+ */
+int get_ucontact(urecord_t* _r, str* _c, str* _callid, str* _path, int _cseq,
+					struct ucontact** _co)
+{
+	ucontact_t* ptr;
+	int no_callid;
+
+	ptr = 0;
+	no_callid = 0;
+	*_co = 0;
+
+	switch (matching_mode) {
+		case CONTACT_ONLY:
+			ptr = contact_match( _r->contacts, _c);
+			break;
+		case CONTACT_CALLID:
+			ptr = contact_callid_match( _r->contacts, _c, _callid);
+			no_callid = 1;
+			break;
+		case CONTACT_PATH:
+			ptr = contact_path_match( _r->contacts, _c, _path);
+			break;
+		default:
+			LM_CRIT("unknown matching_mode %d\n", matching_mode);
+			return -1;
+	}
+
+	if (ptr) {
+		/* found -> check callid and cseq */
+		if ( no_callid || (ptr->callid.len==_callid->len
+		&& memcmp(_callid->s, ptr->callid.s, _callid->len)==0 ) ) {
+			if (_cseq<ptr->cseq)
+				return -1;
+			if (_cseq==ptr->cseq) {
+				get_act_time();
+				return (ptr->last_modified+cseq_delay>act_time)?-2:-1;
+			}
+		}
+		*_co = ptr;
+		return 0;
+	}
+
+	return 1;
+}

+ 192 - 0
modules_k/p_usrloc/urecord.h

@@ -0,0 +1,192 @@
+/*
+ * $Id: urecord.h 5241 2008-11-21 12:52:25Z henningw $ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*! \file
+ *  \brief USRLOC - Usrloc record structure
+ *  \ingroup usrloc
+ */
+
+
+#ifndef URECORD_H
+#define URECORD_H
+
+
+#include <stdio.h>
+#include <time.h>
+#include "hslot.h"
+#include "../../str.h"
+#include "../../qvalue.h"
+#include "ucontact.h"
+
+
+struct hslot; /*!< Hash table slot */
+struct udomain;
+
+/*! \brief
+ * Basic hash table element
+ */
+typedef struct urecord {
+	str* domain;                   /*!< Pointer to domain we belong to 
+                                    * ( null terminated string) */
+	str aor;                       /*!< Address of record */
+	unsigned int aorhash;          /*!< Hash over address of record */
+	ucontact_t* contacts;          /*!< One or more contact fields */
+
+	struct hslot* slot;            /*!< Collision slot in the hash table 
+                                    * array we belong to */
+	struct urecord* prev;          /*!< Next item in the hash entry */
+	struct urecord* next;          /*!< Previous item in the hash entry */
+} urecord_t;
+
+
+/*!
+ * \brief Create and initialize new record structure
+ * \param _dom domain name
+ * \param _aor address of record
+ * \param _r pointer to the new record
+ * \return 0 on success, negative on failure
+ */
+int new_urecord(str* _dom, str* _aor, urecord_t** _r);
+
+
+/*!
+ * \brief Free all memory used by the given structure
+ *
+ * Free all memory used by the given structure.
+ * The structure must be removed from all linked
+ * lists first
+ * \param _r freed record list
+ */
+void free_urecord(urecord_t* _r);
+
+
+/*!
+ * \brief Print a record, useful for debugging
+ * \param _f print output
+ * \param _r printed record
+ */
+void print_urecord(FILE* _f, urecord_t* _r);
+
+
+/*!
+ * \brief Add a new contact in memory
+ *
+ * Add a new contact in memory, contacts are ordered by:
+ * 1) q value, 2) descending modification time
+ * \param _r record this contact belongs to
+ * \param _c contact
+ * \param _ci contact information
+ * \return pointer to new created contact on success, 0 on failure
+ */
+ucontact_t* mem_insert_ucontact(urecord_t* _r, str* _c, ucontact_info_t* _ci);
+
+
+/*!
+ * \brief Remove the contact from lists in memory
+ * \param _r record this contact belongs to
+ * \param _c removed contact
+ */
+void mem_remove_ucontact(urecord_t* _r, ucontact_t* _c);
+
+
+/*!
+ * \brief Remove contact in memory from the list and delete it
+ * \param _r record this contact belongs to
+ * \param _c deleted contact
+ */
+void mem_delete_ucontact(urecord_t* _r, ucontact_t* _c);
+
+
+/*!
+ * \brief Run timer functions depending on the db_mode setting.
+ *
+ * Helper function that run the appropriate timer function, depending
+ * on the db_mode setting.
+ * \param _r processed record
+ */
+void timer_urecord(urecord_t* _r);
+
+
+/*!
+ * \brief Delete a record from the database
+ * \param _r deleted record
+ * \return 0 on success, -1 on failure
+ */
+int db_delete_urecord(struct udomain* _d, urecord_t* _r);
+
+
+/* ===== Module interface ======== */
+
+
+/*!
+ * \brief Release urecord previously obtained through get_urecord
+ * \warning Failing to calls this function after get_urecord will
+ * result in a memory leak when the DB_ONLY mode is used. When
+ * the records is later deleted, e.g. with delete_urecord, then
+ * its not necessary, as this function already releases the record.
+ * \param _r released record
+ */
+typedef void (*release_urecord_t)(urecord_t* _r);
+void release_urecord(urecord_t* _r);
+
+
+/*!
+ * \brief Create and insert new contact into urecord
+ * \param _r record into the new contact should be inserted
+ * \param _contact contact string
+ * \param _ci contact information
+ * \param _c new created contact
+ * \return 0 on success, -1 on failure
+ */
+typedef int (*insert_ucontact_t)(urecord_t* _r, str* _contact,
+		ucontact_info_t* _ci, ucontact_t** _c);
+int insert_ucontact(urecord_t* _r, str* _contact,
+		ucontact_info_t* _ci, ucontact_t** _c);
+
+
+/*!
+ * \brief Delete ucontact from urecord
+ * \param _r record where the contact belongs to
+ * \param _c deleted contact
+ * \return 0 on success, -1 on failure
+ */
+typedef int (*delete_ucontact_t)(urecord_t* _r, struct ucontact* _c);
+int delete_ucontact(urecord_t* _r, struct ucontact* _c);
+
+
+/*!
+ * \brief Get pointer to ucontact with given contact
+ * \param _r record where to search the contacts
+ * \param _c contact string
+ * \param _callid callid
+ * \param _cseq CSEQ number
+ * \param _co found contact
+ * \return 0 - found, 1 - not found, -1 - invalid found, 
+ * -2 - found, but to be skipped (same cseq)
+ */
+typedef int (*get_ucontact_t)(urecord_t* _r, str* _c, str* _callid, str* path, int _cseq,
+		struct ucontact** _co);
+int get_ucontact(urecord_t* _r, str* _c, str* _callid, str* path,  int _cseq,
+		struct ucontact** _co);
+
+
+#endif

+ 82 - 0
modules_k/p_usrloc/usrloc.c

@@ -0,0 +1,82 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ========
+ *
+ * 2006-11-28 Added a new function to the usrloc_api, to retrieve the number
+ *            of registered users.  (Jeffrey Magder - SOMA Networks)
+ */
+
+/*! \file
+ *  \brief USRLOC - module API exports interfaces
+ *  \ingroup usrloc
+ *
+ * - Module \ref usrloc
+ */
+
+#include "usrloc.h"
+#include "../../sr_module.h"
+#include "ul_mod.h"
+
+/*! nat branch flag */
+extern unsigned int nat_bflag;
+/*! flag to protect against wrong initialization */
+extern unsigned int init_flag;
+
+
+/*!
+ * \brief usrloc module API export bind function
+ * \param api usrloc API
+ * \return 0 on success, -1 on failure
+ */
+int bind_usrloc(usrloc_api_t* api)
+{
+	if (!api) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+	LM_ERR("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n");
+	if (init_flag==0) {
+		LM_ERR("configuration error - trying to bind to usrloc module"
+				" before being initialized\n");
+		return -1;
+	}
+
+	api->register_udomain   = register_udomain;
+	api->insert_urecord     = insert_urecord;
+	api->delete_urecord     = delete_urecord;
+	api->get_urecord        = get_urecord;
+	api->lock_udomain       = lock_udomain;
+	api->unlock_udomain     = unlock_udomain;
+	api->release_urecord    = release_urecord;
+	api->insert_ucontact    = insert_ucontact;
+	api->delete_ucontact    = delete_ucontact;
+	api->get_ucontact       = get_ucontact;
+	api->update_ucontact    = update_ucontact;
+	api->register_ulcb      = register_ulcb;
+
+	api->use_domain = use_domain;
+	api->db_mode    = db_mode;
+	api->nat_flag   = nat_bflag;
+
+	return 0;
+}

+ 69 - 0
modules_k/p_usrloc/usrloc.h

@@ -0,0 +1,69 @@
+/*
+ * $Id: usrloc.h 5193 2008-11-13 10:21:53Z henningw $
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*! \file
+ *  \brief USRLOC - module API exports interface
+ *  \ingroup usrloc
+ */
+
+#ifndef USRLOC_H
+#define USRLOC_H
+
+
+#include "dlist.h"
+#include "udomain.h"
+#include "urecord.h"
+#include "ucontact.h"
+#include "ul_callback.h"
+
+
+/*! usrloc API export structure */
+typedef struct usrloc_api {
+	int           use_domain; /*! use_domain module parameter */
+	int           db_mode;    /*! db_mode module parameter */
+	unsigned int  nat_flag;   /*! nat_flag module parameter */
+
+	register_udomain_t   register_udomain;
+	get_all_ucontacts_t  get_all_ucontacts;
+
+	insert_urecord_t     insert_urecord;
+	delete_urecord_t     delete_urecord;
+	get_urecord_t        get_urecord;
+	lock_udomain_t       lock_udomain;
+	unlock_udomain_t     unlock_udomain;
+
+	release_urecord_t    release_urecord;
+	insert_ucontact_t    insert_ucontact;
+	delete_ucontact_t    delete_ucontact;
+	get_ucontact_t       get_ucontact;
+
+	update_ucontact_t    update_ucontact;
+
+	register_ulcb_t      register_ulcb;
+} usrloc_api_t;
+
+
+/*! usrloc API export bind function */
+typedef int (*bind_usrloc_t)(usrloc_api_t* api);
+
+
+#endif

+ 42 - 0
modules_k/p_usrloc/utime.c

@@ -0,0 +1,42 @@
+/*
+ * $Id$
+ *
+ * Usrloc time related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*! \file
+ *  \brief USRLOC - time related functions
+ *  \ingroup usrloc
+ *
+ * - Module \ref usrloc
+ */
+
+#include "utime.h"
+
+
+time_t act_time;
+
+
+void get_act_time(void)
+{
+
+	act_time = time(0);
+}

+ 45 - 0
modules_k/p_usrloc/utime.h

@@ -0,0 +1,45 @@
+/*
+ * $Id: utime.h 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Usrloc time related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*! \file
+ *  \brief USRLOC - time related functions
+ *  \ingroup usrloc
+ */
+
+#ifndef UTIME_H
+#define UTIME_H
+
+#include <time.h>
+
+
+extern time_t act_time;
+
+
+/*! \brief
+ * Get actual time
+ */
+void get_act_time(void);
+
+
+#endif /* UTIME_H */