Browse Source

Initial checkin.

Greg Fausak 22 years ago
commit
3175f89b8f

+ 12 - 0
modules/db_postgres/Makefile

@@ -0,0 +1,12 @@
+# $Id$
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+auto_gen=
+NAME=postgres.so
+
+# libpq-fe.h locations
+DEFS +=-I/usr/local/pgsql/include
+LIBS=-L/usr/local/pgsql/lib -lpq
+
+include ../../Makefile.modules

+ 39 - 0
modules/db_postgres/README

@@ -0,0 +1,39 @@
+# $Id$
+#
+# README
+#
+# History:
+# --------
+# 2003-04-07 this is a hack, from serctl, to make work with postgres
+#
+# DISCLAIMER:
+# I have yet to get this software working in my production environment.
+# Everything compiles and runs, but not for long.  It may or may not be a
+# good starting point for the ser postgres driver.
+#
+# I had a hard time with memory while developing this.
+# So, I decided to incorporate some memory routines that
+# I have been using for years.  This didn't fix the problem,
+# but it did make memory management easier.
+#
+# The postgres driver requires that you have postgres installed
+# on your system.  You can download this from: www.postgresql.org.
+# For this I used postgres version 7.3.2, but I imagine almost any
+# recent version should work.
+#
+# You will then need to have a postgres database somewhere, and it
+# needs to have the tables created in it. Currently I am supporting
+# 2 tables, location and subscriber.  The file createtables.txt contains
+# the table definitions and index definitions.
+# 
+# You may wish to load the tables from a 0.8.10 database.  The script
+# copy_to_psql can be modified to open your mysql database and it outputs
+# the necessary commands to populate the tables of the postgres database.
+#
+# To use the postgres module in your ser.cfg file is just like using the
+# mysql module. Ie:
+# loadmodule "/usr/local/lib/ser/modules/postgres.so"
+# 
+# That's it.  Good luck.
+# ---greg
+# Greg Fausak, August.Net Services, [email protected]

+ 630 - 0
modules/db_postgres/aug_alloc.c

@@ -0,0 +1,630 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+/*
+** ________________________________________________________________________
+**
+**
+**                      $RCSfile$
+**                     $Revision$
+**
+**             Last change $Date$
+**           Last change $Author$
+**                        $State$
+**                       $Locker$
+**
+**               Original author: Andrew Fullford
+**
+**           Copyright (C) August Associates  1995
+**
+** ________________________________________________________________________
+*/
+
+#include "aug_std.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef double MemAlign;
+typedef augUInt32 MemMagic;
+typedef union MemHead MemHead;
+typedef struct MemOpt MemOpt;
+typedef struct MemDestructor MemDestructor;	/* not yet implemented */
+
+/*
+**  One of these MemHead structs is allocated at the head of
+**  each alloc, plus an extra magic number at the end area.
+**  This gives an allocation overhead of:
+**
+**	malloc_overhead + sizeof MemHead + sizeof MemMagic
+**
+**  "Notes" entry for the man page: the allocation overhead is way
+**  too high.  (On a 32bit machine and assuming a malloc overhead
+**  of 8 bytes, the total will be 8 + 32 + 4 = 44 bytes).
+*/
+struct MemHeadStruct
+{
+	MemHead *parent, *sibling, *child;
+	MemOpt *options;
+	char *end;
+	char *file;
+	augUInt32 line;
+	MemMagic magic;
+};
+
+/*
+**  Attempt to guarantee alignment.
+*/
+union MemHead
+{
+	struct MemHeadStruct m;
+	MemAlign align[1];
+};
+
+/*
+**  MemOpt holds optional features.  The only current example
+**  is the memory destructor state.
+*/
+struct MemOpt
+{
+	MemMagic magic;
+	MemDestructor *destructor_list;
+};
+
+/*
+**  These magic numbers are used to validate headers, force memory
+**  to a known state, etc.
+*/
+#define MEM_MAGIC_BOUND	0xC0EDBABE
+#define MEM_MAGIC_FILL	0xDEADC0DE
+
+static int mem_bad(MemHead *mem, char *where, char *file, int line)
+{
+	aug_abort(file, line, "Corrupted memory in %s", where);
+}
+
+/*
+**  Calculate the MemHead address given an aug_alloc() pointer.
+*/
+#define MEM_CROWN(alloc) ((MemHead *)(((char *)alloc) - sizeof (MemHead)))
+#define MEM_DECAPITATE(mem) ((void *)(((char *)mem) + sizeof (MemHead)))
+
+static MemMagic mem_magic = MEM_MAGIC_BOUND;
+#define MEM_TAIL(p) (memcmp((p)->m.end,(char*)&mem_magic,sizeof mem_magic)==0)
+#define MEM_CHECK(p,w) ((p) && \
+			((p)->m.magic != MEM_MAGIC_BOUND || !MEM_TAIL(p)) && \
+			mem_bad((p),(w),file,line))
+
+/*  Initialize stats structure with estimated overhead */
+static augAllocStats mem_stats = {sizeof (MemHead) + sizeof (MemMagic) + 8, 0};
+
+static augNoMemFunc *mem_nomem_func = 0;
+static void mem_nomem(size_t size, char *func, char *file, int line)
+{
+	static augBool active = augFALSE;
+	char *module;
+
+	if(!func)
+		func = "unknown function";
+
+	if(active)
+		fprintf(stderr, "\r\n\nPANIC: nomem bounce\r\n\n");
+	else
+	{
+		active = augTRUE;
+		if(mem_nomem_func)
+			(*mem_nomem_func)(size, func, file, line);
+	}
+
+	fprintf(stderr, "\r\n\n");
+
+	module = aug_module();
+	if(module && *module)
+		fprintf(stderr, "FATAL in %s: ", module);
+	else
+		fprintf(stderr, "FATAL: ");
+
+	fprintf(stderr, "%s failure allocating %lu bytes ", func, size);
+
+	if(file && *file)
+		fprintf(stderr, "from +%d %s \r\n", line, file);
+	else
+		fprintf(stderr, "(unknown location) \r\n");
+
+	fprintf(stderr, "              Current allocations: %10lu \r\n",
+		(mem_stats.alloc_ops - mem_stats.free_ops));
+	fprintf(stderr, "                Total allocations: %10lu \r\n",
+		mem_stats.alloc_ops);
+	fprintf(stderr, "              Total reallocations: %10lu \r\n",
+		mem_stats.realloc_ops);
+	fprintf(stderr, "                      Total frees: %10lu \r\n",
+		mem_stats.free_ops);
+	fprintf(stderr, "Estimated total heap use (KBytes): %10lu \r\n",
+		(mem_stats.current_bytes_allocated +
+		(mem_stats.alloc_ops - mem_stats.free_ops) *
+		mem_stats.estimated_overhead_per_alloc + 512)/1024);
+	fprintf(stderr, "\n");
+		
+	aug_exit(augEXIT_NOMEM);
+}
+
+static void *mem_alloc(size_t size, void *parent, char *file, int line)
+{
+	MemHead *mem, *par;
+	DABNAME("mem_alloc");
+
+	if(parent)
+	{
+		par = MEM_CROWN(parent);
+		MEM_CHECK(par, "parent");
+		MEM_CHECK(par->m.child, "sibling");
+		MEM_CHECK(par->m.sibling, "uncle");
+	}
+	else
+		par = 0;
+
+	mem_stats.current_bytes_allocated += size;
+	mem_stats.alloc_ops++;
+
+	/*  Adjust for overhead  */
+	size += sizeof (MemHead);
+
+	mem = malloc(size + sizeof (MemMagic));
+	if(!mem)
+		mem_nomem(size, "aug_alloc", file, line);
+
+	if(DABLEVEL(DAB_STD))
+	{
+		unsigned long *p;
+		p = (unsigned long *)mem;
+		while((char *)p <= (char *)mem + size)
+			*p++ = MEM_MAGIC_FILL;
+	}
+
+	mem->m.magic = MEM_MAGIC_BOUND;
+	mem->m.file = file;
+	mem->m.line = line;
+	mem->m.end = (char *)mem + size;
+	mem->m.options = 0;
+	mem->m.child = 0;
+	mem->m.parent = par;
+
+	if(par)
+	{
+		if(mem->m.sibling = par->m.child)
+			mem->m.sibling->m.parent = mem;
+		par->m.child = mem;
+	}
+	else
+		mem->m.sibling = 0;
+
+	memcpy(mem->m.end, (char *)&mem_magic, sizeof mem_magic);
+
+	return MEM_DECAPITATE(mem);
+}
+
+static void mem_free(MemHead *mem)
+{
+	size_t size;
+	DABNAME("mem_free");
+
+	while(mem)
+	{
+		MemHead *next = mem->m.sibling;
+		if(mem->m.child)
+			mem_free(mem->m.child);
+		size = (char *)mem->m.end - (char *)mem;
+		size -= sizeof (MemHead) + sizeof (MemMagic);
+		mem_stats.current_bytes_allocated -= size;
+		mem_stats.free_ops++;
+		if(DABLEVEL(DAB_STD))
+		{
+			unsigned long *p = (unsigned long *)(mem+1);
+
+			while((char *)p <= mem->m.end)
+				*p++ = MEM_MAGIC_FILL;
+			p = (unsigned long *)mem;
+			while(p < (unsigned long *)(mem+1))
+				*p++ = MEM_MAGIC_FILL;
+		}
+		free(mem);
+		mem = next;
+	}
+}
+
+static augBool mem_find(MemHead *mem, MemHead *p)
+{
+	while(mem)
+	{
+		MemHead *next;
+
+		if(mem == p)
+			return augTRUE;
+		next = mem->m.sibling;
+		if(mem->m.child)
+			if(mem_find(mem->m.child, p))
+				return augTRUE;
+		mem = next;
+	}
+	return augFALSE;
+}
+
+augExport augNoMemFunc *aug_set_nomem_func(augNoMemFunc *new_func)
+{
+	augNoMemFunc *old = mem_nomem_func;
+	DABNAME("aug_set_nomem_func");
+
+	mem_nomem_func = new_func;
+
+	DABTRACE("New nomem func %08lx, previous %08lx",
+		(unsigned long)mem_nomem_func, (unsigned long)old);
+	return old;
+}
+
+augExport augAllocStats *aug_alloc_stats(void)
+{
+	return &mem_stats;
+}
+
+augExport void *aug_alloc_loc(size_t size, void *parent, char *file, int line)
+{
+	void *alloc;
+	DABNAME("aug_alloc");
+
+	DAB("size %lu, parent %08lx [+%d %s]",
+		(unsigned long)size, (unsigned long)parent, line, file);
+
+	alloc = mem_alloc(size, parent, file, line);
+
+	DABL(80)("size %lu with header, caller mem at %08lx",
+		MEM_CROWN(alloc)->m.end - (char *)MEM_CROWN(alloc),
+		(unsigned long)alloc);
+
+	return alloc;
+}
+
+augExport void *aug_realloc_loc(size_t size, void *prev, char *file, int line)
+{
+	void *alloc;
+	size_t prev_size;
+	MemHead *mem, *par, *kid, *sib, *new;
+	DABNAME("aug_realloc");
+
+	if(!prev)
+		aug_abort(file, line, "Attempt to realloc a NULL pointer");
+
+	mem = MEM_CROWN(prev);
+	MEM_CHECK(mem, "previous alloc");
+
+	par = mem->m.parent;	MEM_CHECK(par, "realloc parent");
+	kid = mem->m.child;	MEM_CHECK(kid, "realloc child");
+	sib = mem->m.sibling;	MEM_CHECK(sib, "realloc sibling");
+
+	prev_size = (mem->m.end - (char *)mem) - sizeof (MemHead);
+
+	DAB("prior size %lu, new %lu [+%d %s]",
+				(unsigned long)prev_size, (unsigned long)size,
+				line, file);
+	DABL(80)("prior mem %08lx", (unsigned long)mem);
+
+	mem_stats.current_bytes_allocated += size - prev_size;
+	mem_stats.realloc_ops++;
+
+	size += sizeof (MemHead);
+
+	new = realloc(mem, size + sizeof (MemMagic));
+	if(!new)
+		mem_nomem(size, "aug_realloc", file, line);
+	new->m.end = (char *)new + size;
+
+	memcpy(new->m.end, (char *)&mem_magic, sizeof mem_magic);
+
+	if(par)
+	{
+		if(par->m.sibling == mem)
+			par->m.sibling = new;
+		else
+			par->m.child = new;
+	}
+	if(kid)
+		kid->m.parent = new;
+	if(sib)
+		sib->m.parent = new;
+
+	alloc = MEM_DECAPITATE(new);
+
+	DABL(80)("size %lu with header, caller mem at %08lx",
+			new->m.end - (char *)new, (unsigned long)alloc);
+
+	return alloc;
+}
+
+augExport void aug_free_loc(void *alloc, char *file, int line)
+{
+	MemHead *mem, *par;
+	DABNAME("aug_free");
+
+	if(!alloc)
+		aug_abort(file, line, "Attempt to free a NULL pointer");
+
+	DAB("Freeing %08lx [+%d %s]", (unsigned long)alloc, line, file);
+
+	mem = MEM_CROWN(alloc);
+	MEM_CHECK(mem, "alloc to free");
+
+	par = mem->m.parent;
+	MEM_CHECK(par, "parent in free");
+
+	if(par)
+	{
+		if(par->m.sibling == mem)
+			par->m.sibling = mem->m.sibling;
+		else
+			par->m.child = mem->m.sibling;
+	}
+
+	if(mem->m.sibling)
+	{
+		mem->m.sibling->m.parent = par;
+		mem->m.sibling = 0;
+	}
+
+	mem_free(mem);
+}
+
+augExport void aug_foster_loc(void *alloc, void *parent, char *file, int line)
+{
+	MemHead *mem, *fpar, *ppar, *sib;
+	DABNAME("aug_foster");
+
+	DAB("Foster %08lx to %08lx [+%d %s]",
+						alloc, parent, line, file);
+
+	if(!alloc)
+		aug_abort(file, line, "Attempt to foster a NULL pointer");
+
+	mem = MEM_CROWN(alloc);
+	MEM_CHECK(mem, "alloc to foster");
+
+	if(parent)
+	{
+		fpar = MEM_CROWN(parent);
+		MEM_CHECK(fpar, "foster parent");
+	}
+	else
+		fpar = 0;
+
+	ppar = mem->m.parent; MEM_CHECK(ppar, "prior parent");
+	sib = mem->m.sibling; MEM_CHECK(ppar, "sibling in foster");
+
+	if(fpar == ppar)
+	{
+		DABTRACE("No change in parent (%08lx)", (unsigned long)fpar);
+		return;
+	}
+
+	if(mem == fpar)
+		aug_abort(file, line, "Attempt to adopt self");
+
+	/*
+	**  Check for incest - isnew parent actually our child?
+	*/
+	if(mem_find(mem->m.child, fpar))
+		aug_abort(file, line, "Attempt to adopt a parent");
+
+	/*
+	**  Leave home.
+	*/
+	if(!ppar)
+	{
+		DABBULK("Leaving orphanage");
+		if(mem->m.sibling)
+			mem->m.sibling->m.parent = 0;
+	}
+	else if(ppar->m.sibling == mem)
+	{
+		DABBULK("Older child");
+		ppar->m.sibling = mem->m.sibling;
+		if(ppar->m.sibling)
+			ppar->m.sibling->m.parent = ppar;
+	}
+	else
+	{
+		DABBULK("Youngest child");
+		ppar->m.child = mem->m.sibling;
+		if(ppar->m.child)
+			ppar->m.child->m.parent = ppar;
+	}
+
+	/*
+	**  Find new home.
+	*/
+	mem->m.parent = fpar;
+	if(fpar)
+	{
+		mem->m.sibling = fpar->m.child;
+		fpar->m.child = mem;
+		if(mem->m.sibling)
+			mem->m.sibling->m.parent = mem;
+	}
+	else
+		mem->m.sibling = 0;
+}
+
+augExport char *aug_strdup_loc(char *str, void *parent, char *file, int line)
+{
+	char *new;
+	size_t size;
+	DABNAME("aug_strdup");
+
+	if(!str)
+		aug_abort(file, line, "Attempt to duplicate a NULL string");
+
+	size = strlen(str)+1;
+	DAB("string length %lu [+%d %s]", (unsigned long)size, line, file);
+
+	new = mem_alloc(size, parent, file, line);
+
+	DABL(80)("size %lu with header, caller mem at %08lx",
+		MEM_CROWN(new)->m.end - (char *)MEM_CROWN(new),
+		(unsigned long)new);
+
+	strcpy(new, str);
+	return new;
+}
+
+augExport char **aug_vecdup_loc(char **vec, void *parent, char *file, int line)
+{
+	char **nv, **v, *c;
+	size_t size;
+	int vsize;
+	DABNAME("aug_vecdup");
+
+	if(!vec)
+		aug_abort(file, line, "Attempt to duplicate a NULL vector");
+
+	size = 0;
+	for(v = vec; *v; v++)
+		size += strlen(*v) + 1;
+	vsize = v - vec;
+	DABL(80)("%d elements, total string size %d", vsize, size);
+
+	vsize++;
+
+	nv = (char **)mem_alloc(vsize * sizeof *v + size, parent, file, line);
+	c = (char *)(nv + vsize);
+	for(v = nv; *vec; v++, vec++)
+	{
+		strcpy(c, *vec);
+		*v = c;
+		c += strlen(c) + 1;
+	}
+	*v = 0;
+
+	return nv;
+}
+
+#ifdef TEST
+static void nomem(size_t size, char *func, char *file, int line)
+{
+	fprintf(stderr, "\nNOMEM on %lu bytes via %s, called from %s line %d\n",
+		(unsigned long)size, func, file, line);
+	/*
+	**  Normally would exit from here, but might as well test the
+	**  default trap.
+	*/
+	return;
+}
+
+main(int argc, char **argv)
+{
+	int i;
+	void *par;
+	char *mem, *m, **v;
+
+	aug_setmodule(argv[0]);
+
+	printf("<MemHead size %lu> should equal <struct MemHead %lu>\n",
+		sizeof (MemHead), sizeof (struct MemHead));
+
+	par = aug_alloc(20, 0);
+	for(i = 0; i < 20; i++)
+	{
+		if(i == 10)
+			mem = aug_strdup("Hello, world\n", par);
+		else
+			(void)aug_alloc(3000, par);
+	}
+
+	mem = aug_realloc(10000, mem);
+
+	for(i = 0; i < 20; i++)
+	{
+		if(i == 10)
+			m = aug_strdup("Hello, world\n", mem);
+		else
+			(void)aug_alloc(3000, par);
+	}
+
+	v = aug_vecdup(argv, par);
+
+	printf("Program args:");
+	while(*v)
+	{
+		printf(" %s", *v++);
+		fflush(stdout);
+	}
+	printf("\n");
+
+	aug_foster(m, par);
+
+	if(argc > 1)
+	{
+		printf("Checking anti-incest test ... this should abort\n");
+		aug_foster(par, mem);
+	}
+
+	for(i = 0; i < 20; i++)
+	{
+		if(i == 10)
+			m = aug_strdup("Hello, world\n", mem);
+		else
+			(void)aug_alloc(3000, mem);
+	}
+
+	mem = aug_realloc(10000, mem);
+
+	aug_foster(m, par);
+
+	aug_free(mem);
+
+	printf("If you can read this, the test completed ok\n");
+	printf("Now testing NOMEM func - this should abort after a while ... ");
+	fflush(stdout);
+
+	aug_set_nomem_func(nomem);
+
+	while(m = aug_alloc(128*1024, par))
+		continue;
+
+	/* Should never get to here */
+
+	aug_free(par);
+
+	aug_exit(augEXIT_YES);
+}
+#endif

+ 94 - 0
modules/db_postgres/aug_alloc.h

@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+/*
+** ________________________________________________________________________
+**
+**
+**                      $RCSfile$
+**                     $Revision$
+**
+**             Last change $Date$
+**           Last change $Author$
+**                        $State$
+**                       $Locker$
+**
+**               Original author: Andrew Fullford
+**
+**           Copyright (C) August Associates  1995
+**
+** ________________________________________________________________________
+*/
+
+/*  AM_TYPE: (INSTALL_INC)  */
+
+#ifndef AUG_ALLOC_H
+#define AUG_ALLOC_H
+
+#include <stdlib.h>
+
+typedef struct
+{
+	int estimated_overhead_per_alloc;	/* assumes malloc overhead
+						   is 8 bytes.  This is
+						   probably low */
+	unsigned long alloc_ops;		/* Total allocs since epoch */
+	unsigned long free_ops;			/* Total frees since epoch */
+	unsigned long realloc_ops;		/* Total reallocs since epoch */
+	unsigned long current_bytes_allocated;	/* Running allocation total */
+} augAllocStats;
+
+#define aug_alloc(s,p) aug_alloc_loc((s),(p),augDAB__FILE__,augDAB__LINE__)
+#define aug_realloc(s,m) aug_realloc_loc((s),(m),augDAB__FILE__,augDAB__LINE__)
+#define aug_strdup(s,p) aug_strdup_loc((s),(p),augDAB__FILE__,augDAB__LINE__)
+#define aug_vecdup(v,p) aug_vecdup_loc((v),(p),augDAB__FILE__,augDAB__LINE__)
+#define aug_free(m) aug_free_loc((m),augDAB__FILE__,augDAB__LINE__)
+#define aug_foster(m,p) aug_foster_loc((m),(p),augDAB__FILE__,augDAB__LINE__)
+
+typedef void augNoMemFunc(size_t size, char *func, char *file, int line);
+extern augNoMemFunc *aug_set_nomem_func(augNoMemFunc *new_func);
+
+extern void *aug_alloc_loc(size_t size, void *parent, char *file, int line);
+extern void *aug_realloc_loc(size_t size, void *prev, char *file, int line);
+extern char *aug_strdup_loc(char *str, void *parent, char *file, int line);
+extern char **aug_vecdup_loc(char **vec, void *parent, char *file, int line);
+extern void aug_free_loc(void *mem, char *file, int line);
+extern void aug_foster_loc(void *mem, void *new_parent, char *file, int line);
+
+extern augAllocStats *aug_alloc_stats(void);
+
+#endif /* AUG_ALLOC_H */

+ 153 - 0
modules/db_postgres/aug_debug.h

@@ -0,0 +1,153 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+/*
+** ________________________________________________________________________
+**
+**
+**                      $RCSfile$
+**                     $Revision$
+**
+**             Last change $Date$
+**           Last change $Author$
+**                        $State$
+**                       $Locker$
+**
+**               Original author: Andrew Fullford
+**
+**           Copyright (C) August Associates  1995
+**
+** ________________________________________________________________________
+*/
+
+/*  AM_TYPE: (INSTALL_INC)  */
+
+#ifndef AUG_DEBUG_H
+#define AUG_DEBUG_H
+
+
+#ifdef DEBUG
+
+#define augDAB_DEBUG	augTRUE
+
+#ifdef __FILE__
+#define augDAB__FILE__	__FILE__
+#else
+#define augDAB__FILE__	""
+#endif
+#ifdef __LINE__
+#define augDAB__LINE__	__LINE__
+#else
+#define augDAB__LINE__	0
+#endif
+
+#else
+
+#define augDAB_DEBUG	augFALSE
+#define augDAB__FILE__	""
+#define augDAB__LINE__	0
+
+#endif /* DEBUG */
+
+/*
+**  Debugging levels.
+**
+**  Each function is tagged with a debugging level.  The initial
+**  state is ``don't know'' which will trigger a match, a relatively
+**  slow operation.  The result of the match is recorded so future
+**  accesses will occur at integer test instruction speed.
+*/
+#define DAB_UNKNOWN	-1		/* Need to perform match */
+#define DAB_OFF		0		/* Matched, but debugs disabled */
+#define DAB_TRACE	25		/* Level implied by DABTRACE() macro */
+#define DAB_STD		50		/* Level implied by DAB() macro */
+#define DAB_BULK	75		/* Level implied by DABBULK() macro */
+
+#if augDAB_DEBUG == augTRUE
+
+#define DABNAME(name)	static char *aug_dab_func=name,			\
+				    aug_dab_file[]=augDAB__FILE__;	\
+			static short aug_dab_level=DAB_UNKNOWN, aug_dab_dummy
+
+#define DABLEVEL(lev)							\
+	(aug_dab_enabled &&						\
+	 ((aug_dab_level == DAB_UNKNOWN &&				\
+	   aug_dab_match(aug_dab_func,aug_dab_file,			\
+			 augDAB__LINE__,&aug_dab_level) >= (lev)) ||	\
+	  aug_dab_level >= (lev)) &&					\
+	 aug_dab_pushinfo(aug_dab_func,aug_dab_file,augDAB__LINE__))
+
+#define DABL(lev)	aug_dab_dummy = DABLEVEL(lev) && aug_dab_fmt
+#define DAB		DABL(DAB_STD)
+#define DABTRACE	DABL(DAB_TRACE)
+#define DABBULK		DABL(DAB_BULK)
+#define DABTEXT		aug_dab_text
+#define DABDUMP		aug_dab_dump
+
+#define DABSET(line)	aug_dab_set(line)
+#define DABRESET()	aug_dab_reset()
+#define DABLOAD(file)	aug_dab_load(file)
+
+extern augBool aug_dab_enabled;
+
+#else
+
+#define DABNAME(name)
+#define DABLEVEL(lev)	(augFALSE)
+#define DABL(lev)	(augFALSE) &&
+#define DAB		DABL(0)
+#define DABTRACE	DABL(0)
+#define DABBULK		DABL(0)
+#define DABTEXT		DABL(0)
+#define DABDUMP		DABL(0)
+
+#define DABSET(line)
+#define DABRESET()
+#define DABLOAD(file)
+
+#endif /* augDAB_DEBUG */
+
+extern int aug_dab_match(char *func, char *file, int line, short *plevel);
+extern augBool aug_dab_pushinfo(char *func, char *file, int line);
+extern augBool aug_dab_fmt(char *fmt, ...);
+extern void aug_dab_text(char *text);
+extern void aug_dab_dump(char *data, int size);
+extern void aug_dab_reset(void);
+extern augBool aug_dab_set(char *line);
+extern void aug_dab_load(char *file);
+
+#endif /* AUG_DEBUG_H */

+ 154 - 0
modules/db_postgres/aug_std.h

@@ -0,0 +1,154 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+/*
+** ________________________________________________________________________
+**
+**
+**                      $RCSfile$
+**                     $Revision$
+**
+**             Last change $Date$
+**           Last change $Author$
+**                        $State$
+**                       $Locker$
+**
+**               Original author: Andrew Fullford
+**
+**           Copyright (C) August Associates  1995
+**
+** ________________________________________________________________________
+*/
+
+/*  AM_TYPE: (INSTALL_INC)  */
+
+#ifndef AUG_STD_H
+#define AUG_STD_H
+
+#include "aug_sysdep.h"
+
+/*
+**  The rest of the libaug package has dependencies on these
+**  system includes.
+*/
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <time.h>
+
+/*
+**  Some boolean verbosity.
+*/
+#ifndef augBool
+#define augBool		char
+#endif
+#define augFALSE	0
+#define augTRUE		1
+
+/*
+**  Exit codes.
+**
+**  To just indicate success or failure, use augEXIT_OK and augEXIT_BAD.
+**  For more esoteric codes, use the others.  Their numbers are chosen
+**  so that they are unlikely to be generated by other applications, and
+**  also to avoid signal numbers or error counts.
+**
+**  Note that augEXIT_NOMEM will be generated by aug_alloc failures
+**  if they are not handled.
+*/
+#define augEXIT_YES	0
+#define augEXIT_NO	1
+#define augEXIT_NOMEM	99
+#define augEXIT_ABORT	(augEXIT_NOMEM-1)
+#define augEXIT_FATAL	(augEXIT_NOMEM-2)
+#define augEXIT_ERROR	(augEXIT_NOMEM-3)
+#define augEXIT_WARN	(augEXIT_NOMEM-4)
+#define augEXIT_USAGE	(augEXIT_NOMEM-5)
+
+/*
+**  Used as a tag to explicitly signify externally accessible symbols.
+**  I don't like leaving off the storage class; it's just too quiet.
+*/
+#define augExport
+
+/*
+**  Some utility prototypes.
+*/
+extern char *aug_cpy(char *tgt, char *src, int size);
+extern char *aug_cat(char *tgt, char *src, int size);
+extern char *aug_strip(char *tgt);
+extern char *aug_upper(char *tgt);
+extern char *aug_lower(char *tgt);
+extern int aug_strexpand(char *tgt, char *src, int tgt_size);
+extern char *aug_getsym(char *name, char *default_value);
+extern void aug_abort_va(char *file, int line, char *fmt, va_list ap);
+extern void aug_abort(char *file, int line, char *fmt, ...);
+extern void aug_exit(int exit_code);
+extern void aug_setmodule(char *name);
+extern char *aug_module(void);
+extern void aug_setenv(char *name, char *value);
+extern char *aug_tempnam(char *dir, char *pfx, void *parent);
+
+extern char *aug_hostname(void);
+extern char *aug_domain(void);
+extern char *aug_fqdn(char *addr);
+
+extern int aug_getport(char *name);
+
+extern void aug_sockopts(int s);
+
+extern int aug_signal_max(void);
+extern int aug_signal_num(char *signame);
+extern char *aug_signal_name(int sig);
+
+extern int aug_utc_offset(time_t tim);
+
+extern unsigned long aug_random(void);
+extern void aug_srandom(unsigned long seed);
+extern unsigned long aug_random_seed(int entropy);
+extern int aug_mkdir(char *name, int mode);
+
+extern char *aug_encrypt(char *type, char *key, char *string, void *parent);
+extern char *aug_decrypt(char *key, char *string, void *parent);
+
+extern char **aug_mxresolv(char *domain, int timeout, int retry, void *parent);
+extern char *aug_mxget(char *domain);
+
+#include "aug_debug.h"
+#include "aug_alloc.h"
+
+#endif /* AUG_STD_H */

+ 330 - 0
modules/db_postgres/aug_sysdep.h

@@ -0,0 +1,330 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+/*
+** ________________________________________________________________________
+**
+**
+**                      $RCSfile$
+**                     $Revision$
+**
+**             Last change $Date$
+**           Last change $Author$
+**                        $State$
+**                       $Locker$
+**
+**               Original author: Andrew Fullford
+**
+**           Copyright (C) August Associates  1995
+**
+** ________________________________________________________________________
+*/
+
+/*  AM_TYPE: (INSTALL_INC)  */
+
+#ifndef AUG_SYSDEP_H
+#define AUG_SYSDEP_H
+
+/*
+**  As necessary, detect operating system, cpu, and compiler
+**  combinations, and establish defines that describe the
+**  characteristics and requirements for the combination.
+**
+**  As each special case is encountered elsewhere in the code,
+**  a new define should be added here for each affected system.
+**
+**  Defines take names like:
+**
+**	AUG_HAS_xxxx	System has capability xxxx
+**	AUG_NO_xxxx	System doesn't have capability xxxx
+**	AUG_BAD_xxxx	System has xxxx, but it's broken
+**
+**  Every system gets AUG_CONFIGURATION so we can reject unconfigured
+**  compiles.  This should be set to an os/cpu/compiler description.
+*/
+#undef AUG_CONFIGURATION
+
+/*
+**  This list should be maintained as the definitive list of capabilities.
+**  Add each new define here with a description and then in each system
+**  dependent section as appropriate.
+**
+**  Please stick to the "#undef" format -- the aug_sysdep.sh script
+**  uses these to report configurations.
+*/
+#undef AUG_HAS_SELECT_H		/* Select macros in <sys/select.h> instead of
+				   <sys/time.h> or <sys/time.h> */
+#undef AUG_BAD_FD_SET		/* FD_SET et al are broken (HP-UX) */
+
+#undef AUG_HAS_LP		/* SysV style "lp" and "lpstat" commands */
+#undef AUG_HAS_LP_REQUEST	/* Has the /usr/spool/lp/request directory.
+				   Probably only ever in HP-UX */
+#undef AUG_HAS_LPR		/* BSD style "lpr" and "/etc/printcap" */
+#undef AUG_NO_PUTENV		/* Use setenv() instead of putenv() */
+#undef AUG_HAS_PREAD		/* Has pread() (combined seek/read) */
+
+/* If neither AUG_HAS_RAND48 nor AUG_HAS_RANDOM, rand() will be used */
+#undef AUG_HAS_RAND48		/* Has lrand48/srand48 calls */
+#undef AUG_HAS_RANDOM		/* Has random/srandom calls */
+
+#undef AUG_HAS_SINCOS		/* -libm has a fast sincos() implementation */
+#undef AUG_NO_IOVEC		/* Some system may not have readv/writev */
+#undef AUG_NO_TIMES		/* Some system may not have times(2) */
+
+#undef AUG_HAS_PSAX		/* ps takes "-ax" arg to show all procs */
+#undef AUG_NO_TZARG		/* get/settimeofday takes no timezone arg */
+
+#undef AUG_NO_CRYPT_H		/* crypt(3) declared in unistd.h instead of
+				   crypt.h. */
+
+#undef AUG_NO_TERMIOS		/* System does not have the termios interface */
+
+#undef AUG_NO_TERMIO_H		/* No termio.h, only termios.h used */
+
+#undef AUG_NO_DB		/* System doesn't support UCB's db(3) */
+
+#undef AUG_NO_GETPAGESIZE	/* System does not have getpagesize() */
+#undef AUG_NO_PTHREADS		/* System does not have Posix Threads support */
+
+/*
+----------------------------------------------------------------------------
+----- SGI Irix with sgi C --------------------------------------------------
+----------------------------------------------------------------------------
+*/
+
+#if defined(sgi) || defined(__sgi) || defined(__sgi__)
+
+#define AUG_HAS_LP
+#define AUG_CONFIGURATION	"SGI Irix with sgi C"
+#define AUG_HAS_RAND48
+
+typedef unsigned int augUInt32;
+
+#endif /* sgi */
+
+/*
+----------------------------------------------------------------------------
+----- Sun Solaris 2.x on SPARC or x86, with SUNpro C or GCC ----------------
+----------------------------------------------------------------------------
+*/
+#if defined(sun) || defined(__sun) || defined(__sun__)
+
+#define AUG_HAS_LP
+#define AUG_HAS_PREAD
+#define AUG_HAS_RAND48
+
+#if defined(i386) || defined(__i386)
+
+#if defined(__GNUC__)
+#define AUG_CONFIGURATION	"Sun Solaris x86 with GCC"
+#else
+#define AUG_CONFIGURATION	"Sun Solaris x86 with SUNpro C"
+#endif
+
+typedef unsigned int augUInt32;
+
+#endif
+
+#if defined(sparc) || defined(__sparc)
+#if defined(__svr4__) || defined(__SVR4)
+
+#if defined(__GNUC__)
+#define AUG_CONFIGURATION	"Sun Solaris 2.x SPARC with GCC"
+#else
+#define AUG_CONFIGURATION	"Sun Solaris 2.x SPARC with SUNpro C"
+#endif
+#endif /* svr4 */
+
+typedef unsigned int augUInt32;
+
+#endif /* sparc */
+
+#endif /* sun */
+
+/*
+----------------------------------------------------------------------------
+----- Linux x86 with GCC ---------------------------------------------------
+----------------------------------------------------------------------------
+*/
+#ifdef __linux
+
+#define AUG_HAS_LPR
+#define AUG_HAS_RANDOM		/* Actually has AUG_HAS_RAND48 too */
+#define AUG_HAS_PSAX
+
+/* AUG_DEBIAN supplied on cc command line where appropriate */
+#ifndef AUG_DEBIAN
+#define AUG_NO_CRYPT_H
+#endif
+
+#if __GNUC__ <= 2 && __GNUC_MINOR__ <= 7
+/* Basically, assume this is a really of version of Linux -- ie "gomer" */
+#define AUG_NO_PTHREADS
+#endif
+
+#if defined(__i386)
+
+#if defined(__GNUC__)
+#define AUG_CONFIGURATION	"Linux x86 with GCC"
+#endif
+
+typedef unsigned int augUInt32;
+
+#endif /* i386 */
+#endif /* linux */
+
+/*
+----------------------------------------------------------------------------
+----- FreeBSD x86 with GCC -------------------------------------------------
+----------------------------------------------------------------------------
+*/
+#ifdef __FreeBSD__
+
+#define AUG_HAS_LPR
+#define AUG_HAS_RANDOM
+#define AUG_HAS_PSAX
+#define AUG_HAS_PREAD
+#define AUG_NO_CRYPT_H
+#define AUG_NO_TERMIO_H
+#define AUG_NO_DB
+
+/*  FreeBSD lacks these error codes.  */
+#define ENODATA	ENOBUFS
+#define EPROTO	EPROTOTYPE
+#define EUNATCH	ENOPROTOOPT
+
+/*  FreeBSD lacks these termios codes.  */
+#define TCGETS	TIOCGETA
+#define TCSETS	TIOCSETA
+#define TCGETA	TIOCGETA
+#define TCSETA	TIOCSETA
+#define TCSETSW	TIOCSETAW
+#define TCFLSH	TIOCFLUSH
+#define termio termios
+
+#if defined(__i386)
+
+#if defined(__GNUC__)
+#define AUG_CONFIGURATION	"FreeBSD x86 with GCC"
+#endif
+
+typedef unsigned int augUInt32;
+
+#endif /* i386 */
+#endif /* freebsd */
+
+/*
+----------------------------------------------------------------------------
+----- HP-UX pa-risc with HP C ----------------------------------------------
+----------------------------------------------------------------------------
+*/
+
+#ifdef __hpux
+
+#define AUG_BAD_FD_SET			/* Not even fixed in HP-UX 10.x */
+#define AUG_HAS_LP
+#define AUG_HAS_LP_REQUEST
+#define AUG_HAS_RAND48
+#define AUG_HAS_SINCOS
+
+#if !defined(__GNUC__)
+#define AUG_CONFIGURATION	"HP-UX pa-risc with HP C"
+#endif
+
+typedef unsigned int augUInt32;
+
+#endif /* hpux */
+
+/*
+----------------------------------------------------------------------------
+----- AIX Configuration with xlC -------------------------------------------
+----------------------------------------------------------------------------
+*/
+
+#ifdef _AIX
+
+#define AUG_HAS_LP
+#define AUG_HAS_LP_REQUEST
+#define AUG_HAS_SELECT_H
+#define AUG_HAS_RAND48
+#define AUG_NO_CRYPT_H
+
+#if !defined(__GNUC__)
+#define AUG_CONFIGURATION       "AIX Configuration with xlC"
+#endif
+
+typedef unsigned int augUInt32;
+
+#endif /* _AIX */
+
+/*
+----------------------------------------------------------------------------
+----- Sun IUS with GCC (formerly Interactive Unix) -----------------------
+----------------------------------------------------------------------------
+*/
+
+/*
+**  This is only sufficient to build a basic libaug.a so selected
+**  utilities can be ported with relative ease.
+**
+**  None of the folio stuff builds (no unix domain sockets), so when
+**  collecting a fresh copy of $AUG/libaug, run "rm -f *fol*".
+*/
+#ifndef _AIX /* xlC can't handle these expressions */
+#if #system(svr3) && #cpu(i386)
+
+#define AUG_HAS_LP
+#define AUG_HAS_RAND48
+#define AUG_NO_CRYPT_H
+#define AUG_CONFIGURATION	"Sun IUS x86 with GCC"
+
+typedef unsigned int augUInt32;
+
+#include <sys/bsdtypes.h>
+
+#endif /* IUS */
+#endif /* ! _AIX */
+
+/*
+----------------------------------------------------------------------------
+*/
+
+#ifndef AUG_CONFIGURATION
+error: os/cpu/compiler combination not configured in $Source$ $Revision$
+#endif
+
+#endif /* AUG_SYSDEP_H */

+ 132 - 0
modules/db_postgres/aug_util.c

@@ -0,0 +1,132 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+/*
+** ________________________________________________________________________
+**
+**
+**                      $RCSfile$
+**                     $Revision$
+**
+**             Last change $Date$
+**           Last change $Author$
+**                        $State$
+**                       $Locker$
+**
+**               Original author: Andrew Fullford
+**
+**           Copyright (C) August Associates  1995
+**
+** ________________________________________________________________________
+*/
+
+#include "aug_std.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+static char *aug_module_name = 0;
+
+/*
+**  May eventually add ``on exit'' function calls.
+*/
+augExport void aug_exit(int exit_code)
+{
+	DABNAME("aug_exit");
+
+	DABTRACE("Exiting with code %d", exit_code);
+	exit(exit_code);
+}
+
+augExport void aug_abort_va(char *file, int line, char *fmt, va_list ap)
+{
+	DABNAME("aug_abort");
+
+	DAB("ABORT from +%d %s", line, file);
+
+	fflush(stdout);
+	fprintf(stderr, "\r\n\n");
+	if(aug_module_name)
+		fprintf(stderr, "%s: ", aug_module_name);
+	fprintf(stderr, "ABORT: ");
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, " -- from +%d %s\r\n\n", line, file);
+
+	if(DABLEVEL(DAB_TRACE))
+	{
+		fprintf(stderr, "Program terminating via abort()\r\n\n");
+		abort();
+	}
+
+	aug_exit(augEXIT_ABORT);
+}
+
+augExport void aug_abort(char *file, int line, char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	aug_abort_va(file, line, fmt, ap);
+	va_end(ap);
+}
+
+/*
+**  WARNING -- don't use DABs below here, aug_debug.c calls this code.
+*/
+augExport void aug_setmodule(char *name)
+{
+	char *prog;
+	int len;
+
+	if(!name)
+		return;
+
+	if((prog = strrchr(name, '/')) ||
+	   (prog = strrchr(name, '\\')))
+		prog++;
+	else
+		prog = name;
+
+	aug_module_name = malloc(strlen(prog) + 1);
+	strcpy(aug_module_name, prog);
+}
+
+augExport char *aug_module(void)
+{
+	return (aug_module_name ? aug_module_name : "");
+}

+ 67 - 0
modules/db_postgres/con_postgres.h

@@ -0,0 +1,67 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+
+#ifndef CON_POSTGRES_H
+#define CON_POSTGRES_H
+
+#include "libpq-fe.h"
+
+/*
+ * Postgres specific connection data
+ */
+struct con_postgres {
+	char *sqlurl;	/* the url we are connected to, all connection memory
+			   parents from this */
+	PGconn *con;	/* this is the postgres connection */
+	PGresult *res;	/* this is the current result */
+	FILE *fp;	/* debug file output */
+	long tpid;	/* record pid of database opener in case one of */
+			/* the children try to close the database */
+};
+
+#define CON_SQLURL(db_con)    (((struct con_postgres*)((db_con)->tail))->sqlurl)
+#define CON_RESULT(db_con)    (((struct con_postgres*)((db_con)->tail))->res)
+#define CON_CONNECTION(db_con) (((struct con_postgres*)((db_con)->tail))->con)
+#define CON_FP(db_con)        (((struct con_postgres*)((db_con)->tail))->fp)
+#define CON_PID(db_con)       (((struct con_postgres*)((db_con)->tail))->tpid)
+
+#define PLOG(f,s) LOG(L_ERR, "PG[%d] %s %s\n",__LINE__,f,s)
+#define DLOG(f,s) LOG(L_INFO, "PG[%d] %s %s\n",__LINE__,f,s)
+
+#endif /* CON_POSTGRES_H */

+ 133 - 0
modules/db_postgres/copy_to_psql

@@ -0,0 +1,133 @@
+#!/usr/local/bin/perl
+#
+# $Id$
+#
+# sc: ser control; tool for maintaining ser's databases
+#
+# History:
+# --------
+# 2003-04-07 initial attempt at file copy script
+#
+# To-DO:
+# -----
+# - generalization for other than mysql databases
+# - front-end to updating administrative mysql password would
+#   be a convenient thing to have
+#
+# quick and dirty script to copy 0.8.10 mysql location and subscription table
+# out and create insert statements for postgres new CVS version table
+# this script only copies 2 tables, location and subscriber.
+# you may need to modify the open(FD,"sdfdsf") line to suite your needs.
+#
+
+$q = <<EOT;
+select
+	user_id, domain, contact, expires, q, callid, cseq,
+	last_modified
+  from
+	location
+EOT
+if(!open(FD,"/usr/local/mysql/bin/mysql --batch ser -e \"$q\"|"))
+{
+	die("can't open mysql process");
+}
+
+print "/* insert location tuples */\n";
+print "delete from location;\n";
+while(<FD>)
+{
+	chop;
+	($user_id,$domain,$contact,$expires,$q,$callid,$cseq, $lastmodified)=
+		split("\t");
+
+	$i = <<EOT;
+	insert
+	  into
+	  	location
+	     (
+	     	username,
+		domain,
+		contact,
+		expires,
+		q,
+		callid,
+		cseq,
+		last_modified,
+		replicate,
+		state
+	     )
+	values
+	     (
+	     	'$user_id',
+		'$domain',
+		'$contact',
+		'$expires',
+		$q,
+		'$callid',
+		$cseq,
+		'$expires',
+		null,
+		null
+	      );
+EOT
+
+	$i =~ s/\n/ /g;
+	$i =~ s/\t+/ /g;
+	$i =~ s/^\s+//;
+	$i =~ s/\s+$//;
+	print "$i\n";
+}
+$q = <<EOT;
+select
+	phplib_id, user_id, password, first_name, last_name, phone,
+	email_address, datetime_created, datetime_modified, confirmation,
+	flag, sendnotification, greeting, ha1, domain, ha1b, perms,
+	allow_find, timezone
+  from
+	subscriber
+EOT
+if(!open(FD,"/usr/local/mysql/bin/mysql --batch ser -e \"$q\"|"))
+{
+	die("can't open mysql process");
+}
+
+print "/* insert subscriber tuples */\n";
+print "delete from subscriber;\n";
+while(<FD>)
+{
+	chop;
+	( $phplib_id, $user_id, $password, $first_name, $last_name,
+	  $phone, $email_address, $datetime_created, $datetime_modified,
+	  $confirmation, $flag, $sendnotification, $greeting, $ha1,
+	  $domain, $ha1b, $perms, $allow_find, $timezone) =
+		split("\t");
+
+	$i = <<EOT;
+	insert
+	  into
+	  	subscriber
+	     (
+		phplib_id, username, password, first_name,
+		last_name, phone, email_address, datetime_created,
+		datetime_modified, confirmation, flag,
+		sendnotification, greeting, ha1, domain,
+		ha1b, perms, allow_find, timezone
+	     )
+	values
+	     (
+		'$phplib_id', '$user_id', '$password', '$first_name',
+		'$last_name', '$phone', '$email_address', '$datetime_created',
+		'$datetime_created', '$confirmation', '$flag',
+		'$sendnotification', '$greeting', '$ha1', '$domain',
+		'$ha1b', '$perms', '$allow_find', '$timezone'
+	      );
+EOT
+
+	$i =~ s/\n/ /g;
+	$i =~ s/\t+/ /g;
+	$i =~ s/^\s+//;
+	$i =~ s/\s+$//;
+	print "$i\n";
+}
+
+exit 0;

+ 57 - 0
modules/db_postgres/db_con.c

@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+#include <string.h>
+#include "../../db/db_con.h"
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "defs.h"
+#include "con_postgres.h"
+#include "aug_std.h"
+
+/*
+ * Store name of table that will be used by
+ * subsequent database functions
+ */
+int use_table(db_con_t* _h, const char* _t)
+{
+	if(CON_TABLE(_h))
+		aug_free(CON_TABLE(_h));
+	CON_TABLE(_h) = aug_strdup((char *) _t, _h);
+	return 0;
+}

+ 81 - 0
modules/db_postgres/db_mod.c

@@ -0,0 +1,81 @@
+/* 
+ * $Id$ 
+ *
+ * Postgres module interface
+ *
+ * Copyright (C) 2001-2003 Fhg Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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  updated to the new module exports interface (andrei)
+ *  2003-03-16  flags export parameter added (janakj)
+ */
+
+#include <stdio.h>
+#include "../../sr_module.h"
+#include "dbase.h"
+
+
+static int mod_init(void);
+
+
+/*
+ * MySQL database module interface
+ */
+
+
+static cmd_export_t cmds[]={
+	{"~db_use_table",  (cmd_function)use_table,     2, 0, 0},
+	{"~db_init",       (cmd_function)db_init,       1, 0, 0},
+	{"~db_close",      (cmd_function)db_close,      2, 0, 0},
+	{"~db_query",      (cmd_function)db_query,      2, 0, 0},
+	{"~db_raw_query",  (cmd_function)db_raw_query,  2, 0, 0},
+	{"~db_free_query", (cmd_function)db_free_query, 2, 0, 0},
+	{"~db_insert",     (cmd_function)db_insert,     2, 0, 0},
+	{"~db_delete",     (cmd_function)db_delete,     2, 0, 0},
+	{"~db_update",     (cmd_function)db_update,     2, 0, 0},
+	{0,0,0,0,0}
+};
+
+
+
+struct module_exports exports = {	
+	"postgres",
+	cmds,
+	0,   /*  module paramers */
+
+	mod_init, /* module initialization function */
+	0,        /* response function*/
+	0,        /* destroy function */
+	0,        /* oncancel function */
+	0         /* per-child init function */
+};
+
+
+static int mod_init(void)
+{
+	fprintf(stderr, "postgres - initializing\n");
+	return 0;
+}

+ 227 - 0
modules/db_postgres/db_res.c

@@ -0,0 +1,227 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+#include <stdlib.h>
+#include "../../db/db_res.h"
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "defs.h"
+#include "con_postgres.h"
+#include "pg_type.h"
+#include "aug_std.h"
+
+int str2valp(db_type_t _t, db_val_t* _v, const char* _s, int _l, void *_p);
+
+/*
+ * Create a new result structure and initialize it
+ */
+db_res_t* new_result_pg(char *parent)
+{
+	db_res_t* r;
+
+	r = (db_res_t*)aug_alloc(sizeof(db_res_t), parent);
+
+	RES_NAMES(r) = 0;
+	RES_TYPES(r) = 0;
+	RES_COL_N(r) = 0;
+	RES_ROWS(r) = 0;
+	RES_ROW_N(r) = 0;
+
+	return r;
+}
+
+/*
+ * Get and convert columns from a result
+ */
+static inline int get_columns(db_con_t* _h, db_res_t* _r)
+{
+	int n, i;
+
+	n = PQnfields(CON_RESULT(_h));
+
+	if (!n) {
+		LOG(L_ERR, "get_columns(): No columns\n");
+		return -2;
+	}
+
+        RES_NAMES(_r) = (db_key_t*)aug_alloc(sizeof(db_key_t) * n, _r);
+	RES_TYPES(_r) = (db_type_t*)aug_alloc(sizeof(db_type_t) * n, _r);
+	RES_COL_N(_r) = n;
+
+	for(i = 0; i < n; i++) {
+		int ft;
+		RES_NAMES(_r)[i] = aug_strdup(PQfname(CON_RESULT(_h),i),
+			RES_NAMES(_r));
+		switch(ft = PQftype(CON_RESULT(_h),i))
+		{
+			case INT2OID:
+			case INT4OID:
+			case INT8OID:
+				RES_TYPES(_r)[i] = DB_INT;
+			break;
+
+			case FLOAT4OID:
+			case FLOAT8OID:
+			case NUMERICOID:
+				RES_TYPES(_r)[i] = DB_DOUBLE;
+			break;
+
+			case DATEOID:
+			case TIMESTAMPOID:
+			case TIMESTAMPTZOID:
+				RES_TYPES(_r)[i] = DB_DATETIME;
+			break;
+
+			case VARCHAROID:
+				RES_TYPES(_r)[i] = DB_STRING;
+			break;
+			
+			default:
+				LOG(L_ERR, "unknown type %d\n", ft);
+				RES_TYPES(_r)[i] = DB_STRING;
+			break;
+		}		
+	}
+	return 0;
+}
+
+/*
+ * Convert a row from result into db API representation
+ */
+int convert_row_pg(db_con_t* _h, db_res_t* _res, db_row_t* _r, char **row_buf,
+	char *parent)
+{
+	int i;
+
+        ROW_VALUES(_r) = (db_val_t*)aug_alloc(
+		sizeof(db_val_t) * RES_COL_N(_res), parent);
+	ROW_N(_r) = RES_COL_N(_res);
+
+	/* I'm not sure about this */
+	/* PQfsize() gives us the native length in the database, so */
+	/* an int would be 4, not the strlen of the value */
+	/* however, strlen of the value would be easy to strlen() */
+
+	for(i = 0; i < RES_COL_N(_res); i++) {
+		if (str2valp(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]), 
+			    row_buf[i],
+			    PQfsize(CON_RESULT(_h),i),
+			    (void *) ROW_VALUES(_r)) < 0) {
+		LOG(L_ERR, "convert_row_pg(): Error while converting value\n");
+			return -3;
+		}
+	}
+	return 0;
+}
+/*
+ * Convert rows from postgres to db API representation
+ */
+static inline int convert_rows(db_con_t* _h, db_res_t* _r)
+{
+	int n, i, j, k;
+	char **row_buf, *s;
+	n = PQntuples(CON_RESULT(_h));
+	RES_ROW_N(_r) = n;
+	if (!n) {
+		RES_ROWS(_r) = 0;
+		return 0;
+	}
+	RES_ROWS(_r) = (struct db_row*)aug_alloc(sizeof(db_row_t) * n,_r);
+	j = RES_COL_N(_r);
+	row_buf = (char **) aug_alloc(sizeof(char *) * (j + 1), CON_SQLURL(_h));
+
+	/* j is the number of columns in the answer set */
+	/* n is the number of rows in the answer set */
+	for(i = 0; i < n; i++) {
+		for(k = 0; k < j; k++) {
+			if(PQgetisnull(CON_RESULT(_h), i, k)) {
+				/*
+				** I don't believe there is a NULL
+				** representation, so, I'll cheat
+				*/
+				s = "";
+			} else {
+				s = PQgetvalue(CON_RESULT(_h), i, k);
+			}
+			*(row_buf + k) = aug_strdup(s, row_buf);
+		}
+		*(row_buf + k) = (char *) 0;
+
+		/*
+		** ASSERT: row_buf contains an entire row in strings
+		*/
+		if (convert_row_pg(_h, _r, &(RES_ROWS(_r)[i]), row_buf,
+			(char *) RES_ROWS(_r)) < 0) {
+			LOG(L_ERR, "convert_rows(): Error converting row #%d\n",
+				i);
+			RES_ROW_N(_r) = i;
+			aug_free(row_buf);
+			return -4;
+		}
+	}
+	aug_free(row_buf);
+	return 0;
+}
+
+/*
+ * Fill the structure with data from database
+ */
+int convert_result(db_con_t* _h, db_res_t* _r)
+{
+	if (get_columns(_h, _r) < 0) {
+		LOG(L_ERR, "convert_result(): Error getting column names\n");
+		return -2;
+	}
+
+	if (convert_rows(_h, _r) < 0) {
+		LOG(L_ERR, "convert_result(): Error while converting rows\n");
+		return -3;
+	}
+	return 0;
+}
+
+
+/*
+ * Release memory used by a result structure
+ */
+int free_result(db_res_t* _r)
+{
+	aug_free(_r);
+
+	return 0;
+}

+ 143 - 0
modules/db_postgres/db_utils.c

@@ -0,0 +1,143 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+#define _GNU_SOURCE /* To avoid strptime warning */
+
+#include <string.h>
+#include <time.h>
+#include "db_utils.h"
+#include "defs.h"
+
+char* trim(char* _s);
+
+/*
+ * SQL URL parser
+ */
+int parse_sql_url(char* _url, char** _user, char** _pass, 
+		  char** _host, char** _port, char** _db)
+{
+	char* slash, *dcolon, *at, *db_slash;
+
+	*_user = '\0';
+	*_pass = '\0';
+	*_host = '\0';
+	*_port = '\0';
+	*_db   = '\0';
+
+	     /* Remove any leading and trailing spaces and tab */
+	_url = trim(_url);
+
+	if (strlen(_url) < 6) return -1;
+
+	if (*_url == '\0') return -2; /* Empty string */
+
+	slash = strchr(_url, '/');
+	if (!slash) return -3;   /* Invalid string, slashes not found */
+
+	if ((*(++slash)) != '/') {  /* Invalid URL, 2nd slash not found */
+		return -4;
+	}
+
+	slash++;
+
+	at = strchr(slash, '@');
+
+	db_slash = strchr(slash, '/');
+	if (db_slash) {
+		*db_slash++ = '\0';
+		*_db = trim(db_slash);
+	}
+
+	if (!at) {
+		dcolon = strchr(slash, ':');
+		if (dcolon) {
+			*dcolon++ = '\0';
+			*_port = trim(dcolon);
+		}
+		*_host = trim(slash);
+	} else {
+		dcolon = strchr(slash, ':');
+	        *at++ = '\0';
+		if (dcolon) {
+			*dcolon++ = '\0';
+			if (dcolon < at) {   /* user:passwd */
+				*_pass = trim(dcolon);
+				dcolon = strchr(at, ':');
+				if (dcolon) {  /* host:port */
+					*dcolon++ = '\0';
+					*_port = trim(dcolon);
+				}
+			} else {            /* host:port */
+				*_port = trim(dcolon);
+			}
+		}
+		*_host = trim(at);
+		*_user = trim(slash);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Remove any tabs and spaces from the begining and the end of
+ * a string
+ */
+char* trim(char* _s)
+{
+	int len;
+	char* end;
+
+	     /* Null pointer, there is nothing to do */
+	if (!_s) return _s;
+
+	     /* Remove spaces and tabs from the begining of string */
+	while ((*_s == ' ') || (*_s == '\t')) _s++;
+
+	len = strlen(_s);
+
+        end = _s + len - 1;
+
+	     /* Remove trailing spaces and tabs */
+	while ((*end == ' ') || (*end == '\t')) end--;
+	if (end != (_s + len - 1)) {
+		*(end+1) = '\0';
+	}
+
+	return _s;
+}

+ 55 - 0
modules/db_postgres/db_utils.h

@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+
+#ifndef DB_UTILS_H
+#define DB_UTILS_H
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE
+#endif
+#include <time.h>
+
+
+/*
+ * SQL URL parser
+ */
+int parse_sql_url(char* _url, char** _user, char** _pass, 
+		  char** _host, char** _port, char** _db);
+
+#endif

+ 346 - 0
modules/db_postgres/db_val.c

@@ -0,0 +1,346 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "../../db/db_val.h"
+#include "../../dprint.h"
+#include "defs.h"
+#include "db_utils.h"
+#include "con_postgres.h"
+#include "aug_std.h"
+
+
+char *strptime(const char *s, const char *format, struct tm *tm);
+
+/*
+ * Convert a string to integer
+ */
+static inline int str2int(const char* _s, int* _v)
+{
+#ifdef PARANOID
+	if ((!_s) || (!_v)) {
+		LOG(L_ERR, "str2int(): Invalid parameter value\n");
+		return -1;
+	}
+#endif
+	*_v = atoi(_s);
+	return 0;
+}
+
+
+/*
+ * Convert a string to double
+ */
+static inline int str2double(const char* _s, double* _v)
+{
+#ifdef PARANOID
+	if ((!_s) || (!_v)) {
+		LOG(L_ERR, "str2double(): Invalid parameter value\n");
+		return -1;
+	}
+#endif
+	*_v = atof(_s);
+	return 0;
+}
+
+
+/* 
+ * Convert a string to time_t
+ */
+static inline int str2time(const char* _s, time_t* _v)
+{
+	struct tm t;
+#ifdef PARANOID
+	if ((!_s) || (!_v)) {
+		LOG(L_ERR, "str2time(): Invalid parameter value\n");
+		return -1;
+	}
+#endif
+
+	strptime(_s,"%Y-%m-%d %H:%M:%S %z",&t);
+	*_v = mktime(&t);
+
+	return 0;
+}
+
+
+/*
+ * Convert an integer to string
+ */
+static inline int int2str(int _v, char* _s, int* _l)
+{
+#ifdef PARANOID
+	if ((!_s) || (!_l) || (!*_l)) {
+		LOG(L_ERR, "int2str(): Invalid parameter value\n");
+		return -1;
+	}
+#endif
+	*_l = snprintf(_s, *_l, "%-d", _v);
+	return 0;
+}
+
+
+/*
+ * Convert a double to string
+ */
+static inline int double2str(double _v, char* _s, int* _l)
+{
+#ifdef PARANOID
+	if ((!_s) || (!_l) || (!*_l)) {
+		LOG(L_ERR, "double2str(): Invalid parameter value\n");
+		return -1;
+	}
+#endif
+	*_l = snprintf(_s, *_l, "%-10.2f", _v);
+	return 0;
+}
+
+
+/*
+ * Convert time_t to string
+ */
+static inline int time2str(time_t _v, char* _s, int* _l)
+{
+	struct tm *t;
+	int bl;
+#ifdef PARANOID
+	if ((!_s) || (!_l) || (*_l < 2))  {
+		LOG(L_ERR, "Invalid parameter value\n");
+		return -1;
+	}
+#endif
+
+	t = gmtime(&_v);
+
+	if((bl=strftime(_s,(size_t)(*_l)-1,"'%Y-%m-%d %H:%M:%S %z'",t))>0)
+		*_l = bl;
+	
+	return 0;
+}
+
+/*
+ * Does not copy strings
+ */
+int str2valp(db_type_t _t, db_val_t* _v, const char* _s, int _l, void *_p)
+{
+	char dbuf[256];
+#ifdef PARANOID
+	if (!_v) {
+		LOG(L_ERR, "str2valp(): Invalid parameter value\n");
+		return -1;
+	}
+#endif
+
+	if (!_s) {
+		DLOG("str2valp", "got a null value");
+		VAL_TYPE(_v) = _t;
+		VAL_NULL(_v) = 1;
+		return 0;
+	}
+
+	switch(_t) {
+	case DB_INT:
+		sprintf(dbuf, "got int %s", _s);
+		DLOG("str2valp", dbuf);
+		if (str2int(_s, &VAL_INT(_v)) < 0) {
+			LOG(L_ERR, "str2valp(): Error while converting integer value from string\n");
+			return -2;
+		} else {
+			VAL_TYPE(_v) = DB_INT;
+			return 0;
+		}
+		break;
+	
+	case DB_DOUBLE:
+		sprintf(dbuf, "got double %s", _s);
+		DLOG("str2valp", dbuf);
+		if (str2double(_s, &VAL_DOUBLE(_v)) < 0) {
+			LOG(L_ERR, "str2valp(): Error while converting double value from string\n");
+			return -3;
+		} else {
+			VAL_TYPE(_v) = DB_DOUBLE;
+			return 0;
+		}
+		break;
+
+	case DB_STRING:
+		sprintf(dbuf, "got string %s", _s);
+		DLOG("str2valp", dbuf);
+
+		VAL_STRING(_v) = aug_strdup(_s, _p);
+		VAL_TYPE(_v) = DB_STRING;
+
+		return 0;
+
+	case DB_STR:
+		VAL_STR(_v).s = aug_alloc(_l + 1, _p);
+		memcpy(_s, VAL_STR(_v).s, _l);
+		VAL_STR(_v).s[_l] = (char) 0;
+		VAL_STR(_v).len = _l;
+		VAL_TYPE(_v) = DB_STR;
+
+		sprintf(dbuf, "got len string %d %s", _l, _s);
+		DLOG("str2valp", dbuf);
+
+		return 0;
+
+	case DB_DATETIME:
+		sprintf(dbuf, "got time %s", _s);
+		DLOG("str2valp", dbuf);
+		if (str2time(_s, &VAL_TIME(_v)) < 0) {
+			PLOG("str2valp", "error converting datetime");
+			return -4;
+		} else {
+			VAL_TYPE(_v) = DB_DATETIME;
+			return 0;
+		}
+		break;
+
+	case DB_BLOB:
+
+		VAL_STR(_v).s = aug_alloc(_l + 1, _p);
+		memcpy(_s, VAL_STR(_v).s, _l);
+		VAL_STR(_v).s[_l] = (char) 0;
+		VAL_STR(_v).len = _l;
+		VAL_TYPE(_v) = DB_BLOB;
+
+		sprintf(dbuf, "got blob %d", _l);
+		DLOG("str2valp", dbuf);
+
+		return 0;
+	}
+	return -5;
+}
+
+
+/*
+ * Used when converting result from a query
+ */
+int val2str(db_val_t* _v, char* _s, int* _len)
+{
+	int l;
+
+#ifdef PARANOID
+	if ((!_v) || (!_s) || (!_len) || (!*_len)) {
+		LOG(L_ERR, "val2str(): Invalid parameter value\n");
+		return -1;
+	}
+#endif
+	if (VAL_NULL(_v)) {
+		*_len = snprintf(_s, *_len, "NULL");
+		return 0;
+	}
+	
+	switch(VAL_TYPE(_v)) {
+	case DB_INT:
+		if (int2str(VAL_INT(_v), _s, _len) < 0) {
+			LOG(L_ERR, "val2str(): Error while converting string to int\n");
+			return -2;
+		} else {
+			return 0;
+		}
+		break;
+
+	case DB_DOUBLE:
+		if (double2str(VAL_DOUBLE(_v), _s, _len) < 0) {
+			LOG(L_ERR, "val2str(): Error while converting string to double\n");
+			return -3;
+		} else {
+			return 0;
+		}
+		break;
+
+	case DB_STRING:
+		l = strlen(VAL_STRING(_v));
+		LOG(L_ERR, "val2str(): converting %s, %d\n", VAL_STRING(_v), l);
+		if (*_len < (l + 3)) {
+			LOG(L_ERR, "val2str(): Destination buffer too short\n");
+			return -4;
+		} else {
+			*_s++ = '\'';
+			memcpy(_s, VAL_STRING(_v), l);
+			*(_s + l) = '\'';
+			*(_s + l + 1) = '\0'; /* FIXME */
+			*_len = l + 2;
+			return 0;
+		}
+		break;
+
+	case DB_STR:
+		l = VAL_STR(_v).len;
+		if (*_len < (l + 3)) {
+			LOG(L_ERR, "val2str(): Destination buffer too short %d\n", *_len);
+			return -5;
+		} else {
+			*_s++ = '\'';
+			memcpy(_s, VAL_STR(_v).s, l);
+			*(_s + l) = '\'';
+			*(_s + l + 1) = '\0';
+			*_len = l + 2;
+			return 0;
+		}
+		break;
+
+	case DB_DATETIME:
+		if (time2str(VAL_TIME(_v), _s, _len) < 0) {
+			LOG(L_ERR, "val2str(): Error while converting string to time_t\n");
+			return -6;
+		} else {
+			return 0;
+		}
+		break;
+
+	case DB_BLOB:
+		l = VAL_BLOB(_v).len;
+		if (*_len < (l * 2 + 3)) {
+			LOG(L_ERR, "val2str(): Destination buffer too short\n");
+			return -7;
+		} else {
+			     /* WRITE ME */
+			return 0;
+		}			
+		break;
+
+	default:
+		DBG("val2str(): Unknow data type\n");
+		return -7;
+	}
+	return -8;
+}

+ 817 - 0
modules/db_postgres/dbase.c

@@ -0,0 +1,817 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+#define MAXCOLUMNS	512
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "db_utils.h"
+#include "defs.h"
+#include "dbase.h"
+#include "con_postgres.h"
+#include "aug_std.h"
+
+long getpid();
+
+static char sql_buf[SQL_BUF_LEN];
+
+static int submit_query(db_con_t* _h, const char* _s);
+static int connect_db(db_con_t* _h, const char* _db_url);
+static int disconnect_db(db_con_t* _h);
+static int free_query(db_con_t* _h);
+
+/*
+** connect_db	Connect to a database
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by db_init()
+**		char *_db_url	the database to connect to
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+**
+**	Notes :
+**		If currently connected, a disconnect is done first
+**		if this process did the connection, otherwise the
+**		disconnect is not done before the new connect.
+**		This is important, as the process that owns the connection
+**		should clean up after itself.
+*/
+
+static int connect_db(db_con_t* _h, const char* _db_url)
+{
+	char* user, *password, *host, *port, *database;
+
+	if(! _h)
+	{
+		PLOG("connect_db", "must pass db_con_t!");
+		return(-1);
+	}
+
+	if(CON_CONNECTED(_h))
+	{
+		DLOG("connect_db", "disconnect first!");
+		disconnect_db(_h);
+	}
+
+	/*
+	** CON_CONNECTED(_h) is now 0, set by disconnect_db()
+	*/
+
+	/*
+	** Note :
+	** Make a scratch pad copy of given SQL URL.
+	** all memory allocated to this connection is rooted
+	** from this.
+	** This is an important concept.
+	** as long as you always allocate memory using the function:
+	** mem = aug_alloc(size, CON_SQLURL(_h)) or
+	** str = aug_strdup(string, CON_SQLURL(_h))
+	** where size is the amount of memory, then in the future
+	** when CON_SQLURL(_h) is freed (in the function disconnect_db())
+	** all other memory allocated in this manner is freed.
+	** this will keep memory leaks from happening.
+	*/
+	CON_SQLURL(_h) = aug_strdup((char *) _db_url, (char *) _h);
+
+	/*
+	** get the connection parameters parsed from the db_url string
+	** it looks like: sql://username:userpass@dbhost:dbport/dbname
+	** username/userpass : name and password for the database
+	** dbhost :            the host name or ip address hosting the database
+	** dbport :            the port to connect to database on
+	** dbname :            the name of the database
+	*/
+	if(parse_sql_url(CON_SQLURL(_h),
+		&user,&password,&host,&port,&database) < 0)
+	{
+		char buf[256];
+		sprintf(buf, "Error while parsing %s", _db_url);
+		PLOG("connect_db", buf);
+
+		aug_free(CON_SQLURL(_h));
+		return -3;
+	}
+
+	/*
+	** finally, actually connect to the database
+	*/
+	CON_CONNECTION(_h) =
+		PQsetdbLogin(host,port,NULL,NULL,database,user, password);
+
+	if(CON_CONNECTION(_h) == 0
+	    || PQstatus(CON_CONNECTION(_h)) != CONNECTION_OK)
+	{
+		PLOG("connect_db", PQerrorMessage(CON_CONNECTION(_h)));
+		PQfinish(CON_CONNECTION(_h));
+		aug_free(CON_SQLURL(_h));
+		return -4;
+	}
+
+	CON_PID(_h) = getpid();
+
+	/*
+	** all is well, database was connected, we can now submit_query's
+	*/
+	CON_CONNECTED(_h) = 1;
+	return 0;
+}
+
+
+/*
+** disconnect_db	Disconnect a database
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by db_init()
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+**
+**	Notes :
+**		All memory associated with CON_SQLURL is freed.
+**		
+*/
+
+static int disconnect_db(db_con_t* _h)
+{
+	if(! _h)
+	{
+		DLOG("disconnect_db", "null db_con_t, ignored!\n");
+		return(0);
+	}
+
+	/*
+	** free lingering memory tree if it exists
+	*/
+	if(CON_SQLURL(_h))
+	{
+		aug_free(CON_SQLURL(_h));
+		CON_SQLURL(_h) = (char *) 0;
+	}
+
+	/*
+	** ignore if there is no current connection
+	*/
+	if(CON_CONNECTED(_h) != 1)
+	{
+		DLOG("disconnect_db", "not connected, ignored!\n");
+		return 0;
+	}
+
+	/*
+	** make sure we are trying to close a connection that was opened
+	** by our process ID
+	*/
+	if(CON_PID(_h) == getpid())
+	{
+		PQfinish(CON_CONNECTION(_h));
+		CON_CONNECTED(_h) = 0;
+	}
+	else
+	{
+		DLOG("disconnect_db",
+			"attempt to release connection not owned, ignored!\n");
+	}
+
+	return 0;
+}
+
+/*
+** db_init	initialize database for future queries
+**
+**	Arguments :
+**		char *_sqlurl;	sql database to open
+**
+**	Returns :
+**		db_con_t * NULL upon error
+**		db_con_t * if successful
+**
+**	Notes :
+**		db_init must be called prior to any database
+**		functions.
+*/
+
+db_con_t *db_init(const char* _sqlurl)
+{
+	db_con_t* res;
+
+	DLOG("db_init", "entry");
+
+	/*
+	** this is the root memory for this database connection.
+	*/
+	res = aug_alloc(sizeof(db_con_t) + sizeof(struct con_postgres), NULL);
+	memset(res, 0, sizeof(db_con_t) + sizeof(struct con_postgres));
+
+	if (connect_db(res, _sqlurl) < 0)
+	{
+		PLOG("db_init", "Error while trying to open database, FATAL\n");
+		aug_free(res);
+		return((db_con_t *) 0);
+	}
+
+	return res;
+}
+
+
+/*
+** db_close	last function to call when db is no longer needed
+**
+**	Arguments :
+**		db_con_t * the connection to shut down, as supplied by db_init()
+**
+**	Returns :
+**		(void)
+**
+**	Notes :
+**		All memory and resources are freed.
+*/
+
+void db_close(db_con_t* _h)
+{
+	DLOG("db_close", "entry");
+	if(! _h)
+	{
+		PLOG("db_close", "no handle passed, ignored");
+		return;
+	}
+
+	disconnect_db(_h);
+	aug_free(_h);
+
+}
+
+/*
+** submit_query	run a query
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by db_init()
+**		char *_s	the text query to run
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+*/
+
+static int submit_query(db_con_t* _h, const char* _s)
+{	
+	int rv;
+
+	/*
+	** this bit of nonsense in case our connection get screwed up 
+	*/
+	switch(rv = PQstatus(CON_CONNECTION(_h)))
+	{
+		case CONNECTION_OK: break;
+		case CONNECTION_BAD:
+			PLOG("submit_query", "connection reset");
+			PQreset(CON_CONNECTION(_h));
+		break;
+	}
+
+	/*
+	** free any previous query that is laying about
+	*/
+	if(CON_RESULT(_h))
+	{
+		free_query(_h);
+	}
+
+	/*
+	** exec the query
+	*/
+	CON_RESULT(_h) = PQexec(CON_CONNECTION(_h), _s);
+
+	rv = 0;
+	if(PQresultStatus(CON_RESULT(_h)) == 0)
+	{
+		PLOG("submit_query", "initial failure, FATAL");
+		/* 
+		** terrible error??
+		*/
+		rv = -3;
+	}
+	else
+	{
+		/*
+		** the query ran, get the status
+		*/
+		switch(PQresultStatus(CON_RESULT(_h)))
+		{
+			case PGRES_EMPTY_QUERY: rv = -9; break;
+			case PGRES_COMMAND_OK: rv = 0; break;
+			case PGRES_TUPLES_OK: rv = 0; break;
+			case PGRES_COPY_OUT: rv = -4; break;
+			case PGRES_COPY_IN: rv = -5; break;
+			case PGRES_BAD_RESPONSE: rv = -6; break;
+			case PGRES_NONFATAL_ERROR: rv = -7; break;
+			case PGRES_FATAL_ERROR: rv = -8; break;
+			default: rv = -2; break;
+		}
+	}
+	if(rv < 0)
+	{
+		/*
+		** log the error
+		*/
+		char buf[256];
+		sprintf(buf, "query '%s', result '%s'\n",
+			_s, PQerrorMessage(CON_CONNECTION(_h)));
+		PLOG("submit_query", buf);
+	}
+
+	return(rv);
+}
+
+/*
+** free_query	clear the db channel and clear any old query result status
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by db_init()
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+*/
+
+static int free_query(db_con_t* _h)
+{
+	if(CON_RESULT(_h))
+	{
+		PQclear(CON_RESULT(_h));
+		CON_RESULT(_h) = 0;
+	}
+
+	return 0;
+}
+
+/*
+** db_free_query	free the query and free the result memory
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by db_init()
+**		db_res_t *	the result of a query
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+*/
+
+int db_free_query(db_con_t* _h, db_res_t* _r)
+{
+	free_query(_h);
+	free_result(_r);
+
+	return 0;
+}
+
+/*
+** begin_transaction	begin transaction
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by db_init()
+**		char *		this is just in case an error message
+**				is printed, we will know which query
+**				was going to be run, giving us a code debug hint
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+**
+**	Notes :
+**		This function may be called with a messed up communication
+**		channel.  Therefore, alot of this function is dealing with
+**		that.  Wen the layering gets corrected later this stuff
+**		should continue to work correctly, it will just be
+**		way to defensive.
+*/
+
+static int begin_transaction(db_con_t * _h, char *_s)
+{
+	PGresult *mr;
+	int rv;
+
+	/*
+	** Note:
+	**  The upper layers of code may attempt a transaction
+	**  before opening or having a valid connection to the
+	**  database.  We try to sense this, and open the database
+	**  if we have the sqlurl in the _h structure.  Otherwise,
+	**  all we can do is return an error.
+	*/
+
+	if(_h)
+	{
+		if(CON_CONNECTED(_h))
+		{
+			mr = PQexec(CON_CONNECTION(_h), "BEGIN");
+			if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
+			{
+				/*
+				** We get here if the connection to the
+				** db is corrupt, which can happen a few
+				** different ways, but all of them are
+				** related to the parent process forking,
+				** or being forked.
+				*/
+				PLOG("begin_transaction","corrupt connection");
+				CON_CONNECTED(_h) = 0;
+			}
+			else
+			{
+				/*
+				** this is the normal way out.
+				** the transaction ran fine.
+				*/
+				PQclear(mr);
+				return(0);
+			}
+		}
+		else
+		{
+			DLOG("begin_transaction", "called before db_init");
+		}
+
+		/*
+		** if we get here we have a corrupt db connection,
+		** but we probably have a valid db_con_t structure.
+		** attempt to open the db.
+		*/
+
+		if((rv = connect_db(_h, CON_SQLURL(_h))) != 0)
+		{
+			/*
+			** our attempt to fix the connection failed
+			*/
+			char buf[256];
+			sprintf(buf, "no connection, FATAL %d!", rv);
+			PLOG("begin_transaction",buf);
+			return(rv);
+		}
+	}
+	else
+	{
+		PLOG("begin_transaction","must call db_init first!");
+		return(-1);
+	}
+
+	/*
+	** we get here if the database connection was corrupt,
+	** i didn't want to use recursion ...
+	*/
+
+	mr = PQexec(CON_CONNECTION(_h), "BEGIN");
+	if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
+	{
+		char buf[256];
+		sprintf("FATAL %s, '%s'!\n",
+			PQerrorMessage(CON_CONNECTION(_h)), _s);
+		PLOG("begin_transaction", buf);
+		return(-1);
+	}
+
+	DLOG("begin_transaction", "db channel reset successful");
+
+	PQclear(mr);
+	return(0);
+}
+
+/*
+** commit_transaction	any begin_transaction must be terminated with this
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by db_init()
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+*/
+
+static int commit_transaction(db_con_t * _h)
+{
+	PGresult *mr;
+
+	mr = PQexec(CON_CONNECTION(_h), "COMMIT");
+	if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
+	{
+		PLOG("commit_transaction", "error");
+		return -1;
+	}
+	PQclear(mr);
+	return(0);
+}
+
+/*
+ * Print list of columns separated by comma
+ */
+static int print_columns(char* _b, int _l, db_key_t* _c, int _n)
+{
+	int i;
+	int res = 0;
+	for(i = 0; i < _n; i++) {
+		if (i == (_n - 1)) {
+			res += snprintf(_b + res, _l - res, "%s ", _c[i]);
+		} else {
+			res += snprintf(_b + res, _l - res, "%s,", _c[i]);
+		}
+	}
+	return res;
+}
+
+
+/*
+ * Print list of values separated by comma
+ */
+static int print_values(char* _b, int _l, db_val_t* _v, int _n)
+{
+	int i, res = 0, l;
+
+	for(i = 0; i < _n; i++) {
+		l = _l - res;
+/*		LOG(L_ERR, "%d sizes l = _l - res %d = %d - %d\n", i, l,_l,res);
+*/
+		if (val2str(_v + i, _b + res, &l) < 0) {
+			LOG(L_ERR,
+			 "print_values(): Error converting value to string\n");
+			return 0;
+		}
+		res += l;
+		if (i != (_n - 1)) {
+			*(_b + res) = ',';
+			res++;
+		}
+	}
+	return res;
+}
+
+
+/*
+ * Print where clause of SQL statement
+ */
+static int print_where(char* _b, int _l, db_key_t* _k,
+	db_op_t* _o, db_val_t* _v, int _n)
+{
+	int i;
+	int res = 0;
+	int l;
+	for(i = 0; i < _n; i++) {
+		if (_o) {
+			res += snprintf(_b + res, _l - res, "%s%s",
+				_k[i], _o[i]);
+		} else {
+			res += snprintf(_b + res, _l - res, "%s=", _k[i]);
+		}
+		l = _l - res;
+		val2str(&(_v[i]), _b + res, &l);
+		res += l;
+		if (i != (_n - 1)) {
+			res += snprintf(_b + res, _l - res, " AND ");
+		}
+	}
+	return res;
+}
+
+
+/*
+ * Print set clause of update SQL statement
+ */
+static int print_set(char* _b, int _l, db_key_t* _k,
+	db_val_t* _v, int _n)
+{
+	int i;
+	int res = 0;
+	int l;
+	for(i = 0; i < _n; i++) {
+		res += snprintf(_b + res, _l - res, "%s=", _k[i]);
+		l = _l - res;
+		val2str(&(_v[i]), _b + res, &l);
+		res += l;
+		if (i != (_n - 1)) {
+			if ((_l - res) >= 1) {
+				*(_b + res++) = ',';
+			}
+		}
+	}
+	return res;
+}
+
+
+/*
+ * Query table for specified rows
+ * _h: structure representing database connection
+ * _k: key names
+ * _op: operators
+ * _v: values of the keys that must match
+ * _c: column names to return
+ * _n: nmber of key=values pairs to compare
+ * _nc: number of columns to return
+ * _o: order by the specified column
+ */
+int db_query(db_con_t* _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, db_res_t** _r)
+{
+	int off, rv;
+	if (!_c) {
+		off = snprintf(sql_buf, SQL_BUF_LEN,
+			"select * from %s ", CON_TABLE(_h));
+	} else {
+		off = snprintf(sql_buf, SQL_BUF_LEN, "select ");
+		off += print_columns(sql_buf + off, SQL_BUF_LEN - off, _c, _nc);
+		off += snprintf(sql_buf + off, SQL_BUF_LEN - off,
+			"from %s ", CON_TABLE(_h));
+	}
+	if (_n) {
+		off += snprintf(sql_buf + off, SQL_BUF_LEN - off, "where ");
+		off += print_where(sql_buf + off, SQL_BUF_LEN - off,
+			_k, _op, _v, _n);
+	}
+	if (_o) {
+		off += snprintf(sql_buf + off, SQL_BUF_LEN - off,
+			"order by %s", _o);
+	}
+
+	if(begin_transaction(_h, sql_buf)) return(-1);
+	if (submit_query(_h, sql_buf) < 0) {
+		LOG(L_ERR, "db_query(): Error while submitting query\n");
+		return -2;
+	}
+	rv = get_result(_h, _r);
+	free_query(_h);
+	commit_transaction(_h);
+	return(rv);
+}
+
+
+/*
+ * Execute a raw SQL query
+ */
+int db_raw_query(db_con_t* _h, char* _s, db_res_t** _r)
+{
+	int rv;
+
+	if(begin_transaction(_h, sql_buf)) return(-1);
+	if (submit_query(_h, _s) < 0) {
+		LOG(L_ERR, "db_raw_query(): Error while submitting query\n");
+		return -2;
+	}
+	rv = get_result(_h, _r);
+	free_query(_h);
+	commit_transaction(_h);
+	return(rv);
+}
+
+/*
+ * Retrieve result set
+ */
+int get_result(db_con_t* _h, db_res_t** _r)
+{
+	*_r = new_result_pg(CON_SQLURL(_h));
+
+	if (!CON_RESULT(_h)) {
+		LOG(L_ERR, "get_result(): error");
+		free_result(*_r);
+		*_r = 0;
+		return -3;
+	}
+
+        if (convert_result(_h, *_r) < 0) {
+		LOG(L_ERR, "get_result(): Error while converting result\n");
+		free_result(*_r);
+		*_r = 0;
+
+		return -4;
+	}
+
+	return 0;
+}
+
+/*
+ * Insert a row into specified table
+ * _h: structure representing database connection
+ * _k: key names
+ * _v: values of the keys
+ * _n: number of key=value pairs
+ */
+int db_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n)
+{
+	int off;
+	off = snprintf(sql_buf, SQL_BUF_LEN, "insert into %s (", CON_TABLE(_h));
+	off += print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n);
+	off += snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values (");
+	off += print_values(sql_buf + off, SQL_BUF_LEN - off, _v, _n);
+	*(sql_buf + off++) = ')';
+	*(sql_buf + off) = '\0';
+
+	if(begin_transaction(_h, sql_buf)) return(-1);
+	if (submit_query(_h, sql_buf) < 0) {
+		LOG(L_ERR, "db_insert(): Error while inserting\n");
+		return -2;
+	}
+	free_query(_h);
+	commit_transaction(_h);
+	return(0);
+}
+
+
+/*
+ * Delete a row from the specified table
+ * _h: structure representing database connection
+ * _k: key names
+ * _o: operators
+ * _v: values of the keys that must match
+ * _n: number of key=value pairs
+ */
+int db_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n)
+{
+	int off;
+	off = snprintf(sql_buf, SQL_BUF_LEN, "delete from %s", CON_TABLE(_h));
+	if (_n) {
+		off += snprintf(sql_buf + off, SQL_BUF_LEN - off, " where ");
+		off += print_where(sql_buf + off, SQL_BUF_LEN - off, _k,
+			_o, _v, _n);
+	}
+	if(begin_transaction(_h, sql_buf)) return(-1);
+	if (submit_query(_h, sql_buf) < 0) {
+		LOG(L_ERR, "db_delete(): Error while deleting\n");
+		return -2;
+	}
+	free_query(_h);
+	commit_transaction(_h);
+	return(0);
+}
+
+
+/*
+ * Update some rows in the specified table
+ * _h: structure representing database connection
+ * _k: key names
+ * _o: operators
+ * _v: values of the keys that must match
+ * _uk: updated columns
+ * _uv: updated values of the columns
+ * _n: number of key=value pairs
+ * _un: number of columns to update
+ */
+int db_update(db_con_t* _h, 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 off;
+	off = snprintf(sql_buf, SQL_BUF_LEN, "update %s set ", CON_TABLE(_h));
+	off += print_set(sql_buf + off, SQL_BUF_LEN - off, _uk, _uv, _un);
+	if (_n) {
+		off += snprintf(sql_buf + off, SQL_BUF_LEN - off, " where ");
+		off += print_where(sql_buf + off, SQL_BUF_LEN - off, _k,
+			_o, _v, _n);
+		*(sql_buf + off) = '\0';
+	}
+
+	if(begin_transaction(_h, sql_buf)) return(-1);
+	if (submit_query(_h, sql_buf) < 0) {
+		LOG(L_ERR, "db_update(): Error while updating\n");
+		return -2;
+	}
+	free_query(_h);
+	commit_transaction(_h);
+	return(0);
+}

+ 109 - 0
modules/db_postgres/dbase.h

@@ -0,0 +1,109 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+
+#ifndef DBASE_H
+#define DBASE_H
+
+#include "../../db/db_con.h"
+#include "../../db/db_res.h"
+#include "../../db/db_key.h"
+#include "../../db/db_op.h"
+#include "../../db/db_val.h"
+
+
+/*
+ * Initialize database connection
+ */
+db_con_t* db_init(const char* _sqlurl);
+
+/*
+ * Close a database connection
+ */
+void db_close(db_con_t* _h);
+
+/*
+ * Return result of previous query
+ */
+int get_result(db_con_t* _h, db_res_t** _r);
+
+/*
+ * create a new result anchored on parent memory
+ */
+db_res_t* new_result_pg(char *parent);
+
+
+/*
+ * Free all memory allocated by get_result
+ */
+int db_free_query(db_con_t* _h, db_res_t* _r);
+
+
+/*
+ * Do a query
+ */
+int db_query(db_con_t* _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, db_res_t** _r);
+
+
+/*
+ * Raw SQL query
+ */
+int db_raw_query(db_con_t* _h, char* _s, db_res_t** _r);
+
+
+/*
+ * Insert a row into table
+ */
+int db_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n);
+
+
+/*
+ * Delete a row from table
+ */
+int db_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
+
+
+/*
+ * Update a row in table
+ */
+int db_update(db_con_t* _h, 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 /* DBASE_H */

+ 46 - 0
modules/db_postgres/defs.h

@@ -0,0 +1,46 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+
+#ifndef DEFS_H
+#define DEFS_H
+
+#define PARANOID
+#define SQL_BUF_LEN 65535
+
+#endif /* DEFS_H */

+ 96 - 0
modules/db_postgres/pg_type.h

@@ -0,0 +1,96 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+#define BOOLOID			16
+#define BYTEAOID		17
+#define CHAROID			18
+#define NAMEOID			19
+#define INT8OID			20
+#define INT2OID			21
+#define INT2VECTOROID	22
+#define INT4OID			23
+#define REGPROCOID		24
+#define TEXTOID			25
+#define OIDOID			26
+#define TIDOID		27
+#define XIDOID 28
+#define CIDOID 29
+#define OIDVECTOROID	30
+#define POINTOID		600
+#define LSEGOID			601
+#define PATHOID			602
+#define BOXOID			603
+#define POLYGONOID		604
+#define LINEOID			628
+#define FLOAT4OID 700
+#define FLOAT8OID 701
+#define ABSTIMEOID		702
+#define RELTIMEOID		703
+#define TINTERVALOID	704
+#define UNKNOWNOID		705
+#define CIRCLEOID		718
+#define CASHOID 790
+#define MACADDROID 829
+#define INETOID 869
+#define CIDROID 650
+#define ACLITEMOID		1033
+#define BPCHAROID		1042
+#define VARCHAROID		1043
+#define DATEOID			1082
+#define TIMEOID			1083
+#define TIMESTAMPOID	1114
+#define TIMESTAMPTZOID	1184
+#define INTERVALOID		1186
+#define TIMETZOID		1266
+#define BITOID	 1560
+#define VARBITOID	  1562
+#define NUMERICOID		1700
+#define REFCURSOROID	1790
+#define REGPROCEDUREOID 2202
+#define REGOPEROID		2203
+#define REGOPERATOROID	2204
+#define REGCLASSOID		2205
+#define REGTYPEOID		2206
+#define RECORDOID		2249
+#define CSTRINGOID		2275
+#define ANYOID			2276
+#define ANYARRAYOID		2277
+#define VOIDOID			2278
+#define TRIGGEROID		2279
+#define LANGUAGE_HANDLEROID		2280
+#define INTERNALOID		2281
+#define OPAQUEOID		2282

+ 783 - 0
modules/db_postgres/serpgctl

@@ -0,0 +1,783 @@
+#!/bin/sh 
+#
+# $Id$
+#
+# sc: ser control; tool for maintaining ser's databases
+#
+# History:
+# --------
+# 2003-04-06 this is a hack, from serctl, to make work with postgres
+# 2003-02-23 Thomas's start|stop commands checked in
+#
+# To-DO:
+# -----
+# - generalization for other than mysql databases
+# - front-end to updating administrative mysql password would
+#   be a convenient thing to have
+#
+# I copy this over the top of the existing serctl program.
+#
+
+
+
+# configuration for starting/stopping ser
+PID_FILE=/var/run/ser.pid
+SYSLOG=1 # 0=output to console, 1=output to syslog
+STARTOPTIONS= # for example -dddd
+
+# ser's FIFO server
+if [ -z "$SER_FIFO" ]; then
+	SER_FIFO=/tmp/ser_fifo
+fi
+# period in which stats are reprinted
+if [ -z "$WATCH_PERIOD" ] ; then
+	WATCH_PERIOD=2
+fi
+PW="test"
+
+# SQL config
+if [ -z "$SQL_DB" ] ; then
+	SQL_DB=ser
+fi
+if [ -z "$SQL_HOST" ] ; then
+	SQL_HOST=localhost
+fi
+if [ -z "$SQL_PORT" ] ; then
+	SQL_PORT=5432
+fi
+if [ -z "$SQL_USER" ] ; then
+	SQL_USER=ser
+fi
+
+# the read-only user for whom password may be stored here
+if [ -z "$RO_USER" ] ; then
+	RO_USER=serro
+fi
+if [ -z "$RO_PW" ] ; then
+	RO_PW=47serro11
+fi
+
+# binaries
+GENHA1='gen_ha1'
+PGSQL='psql'
+SER='sr'
+LAST_LINE='tail -1'
+
+# ACL name verification
+VERIFY_ACL=1
+ACL_GROUPS="local ld int voicemail free-pstn"
+
+# expiration time for alias table
+FOREVER='2020-05-28 21:32:15'
+FOREVER_REL=1073741823
+
+VERSION='$Revision$'
+#### SQL names
+
+# Usr Loc Table
+USRLOC=location
+USER_COLUMN=username
+CALLID_COLUMN=callid
+
+# subscriber table
+TABLE=subscriber
+REALM_COLUMN=domain
+HA1_COLUMN=HA1
+HA1B_COLUMN=HA1B
+PASSWORD_COLUMN=password
+SUBSCRIBER_COLUMN='username'
+EMAIL_COLUMN=email_address
+SUB_CREATED_COLUMN=datetime_created
+SUB_MODIFIED_COLUMN=datetime_modified
+PHP_LIB_COLUMN=phplib_id
+
+# acl table
+ACL_TABLE=grp
+ACL_USER_COLUMN=user
+ACL_GROUP_COLUMN=grp
+ACL_MODIFIED_COLUMN=last_modified
+
+# aliases table
+A_TABLE=aliases
+A_USER_COLUMN=user
+A_CONTACT_COLUMN=contact
+A_EXPIRES_COLUMN=expires
+A_Q_COLUMN=q
+A_CALLID_COLUMN=callid
+A_CSEQ_COLUMN=cseq
+A_LAST_MODIFIED_COLUMN=last_modified
+
+FIFO_DBG=0
+#===================================================================
+
+
+usage() {
+CMD=`basename $0`
+if [ "0$VERIFY_ACL" -eq 1 ] ; then
+	EXTRA_TEXT="ACL privileges are: $ACL_GROUPS"
+fi
+cat <<EOF
+$0 $VERSION
+usage: 
+           * subscribers *
+ $CMD add <username> <password> <email> .. add a new subscriber (*)
+ $CMD passwd <username> <passwd> ......... change user's password (*)
+ $CMD rm <username> ...................... delete a user (*)
+ $CMD mail <username> .................... send an email to a user
+ $CMD alias show [<alias>] ............... show aliases
+ $CMD alias rm <alias> ................... remove an alias
+ $CMD alias add <alias> <uri> ............ add an aliases 
+
+           * access control lists *
+ $CMD acl show [<username>] .............. show user membership
+ $CMD acl grant <username> <group> ....... grant user membership (*)
+ $CMD acl revoke <username> [<group>] .... grant user membership(s) (*)
+
+           * usrloc *
+ $CMD ul show [<username>]................ show in-RAM online users
+ $CMD ul rm <username> ................... delete user's UsrLoc entries
+ $CMD ul add <username> <uri> ............ introduce a permanent UrLoc entry
+ $CMD showdb [<username>] ................ show online users flushed in DB
+
+           * server health *
+ $CMD monitor ............................ show internal status
+ $CMD ps ................................. show runnig processes 
+ $CMD fifo ............................... send raw commands to FIFO
+
+           * server control *
+ $CMD start .............................. start ser
+ $CMD stop ............................... stop ser
+ $CMD restart ............................ restart ser
+
+
+   Commands labeled with (*) will prompt for a MySQL password.
+   If the variable PW is set, the password will not be prompted.
+
+     $EXTRA_TEXT
+
+EOF
+}
+
+# check the parameter if it is a valid SIP URI
+# quite simplified now -- it captures just very basic
+# errors
+check_uri() {
+	echo "$1" | grep -E "^sip:([a-zA-Z0-9_]+@)?.*\..*" 
+}
+
+
+#params: none
+# output: PW
+prompt_pw() {
+	if [ -z "$PW" ] ; then
+		savetty=`stty -g`
+		printf "Postgres password: "
+		stty -echo
+    	read PW
+		stty $savetty
+    	echo
+	fi
+}
+
+# process output from FIFO server; if everything is ok
+# skip the first "ok" line and proceed to returned 
+# parameters
+filter_fl()
+{
+#	tail +2
+	
+	awk 'BEGIN {line=0;IGNORECASE=1;}
+		{line++}
+		line==1 && /^200 ok/ { next }
+		{ print }'
+}
+
+
+fifo_cmd()
+{
+	if [ "0${FIFO_DBG}" -eq 1 ] ; then
+		echo "entering fifo_cmd $*"
+	fi
+	if [ "$#" -lt 1 ]; then
+		echo "ERROR: fifo_cmd must take at least command name as parameter"
+		exit
+	fi
+	name=ser_receiver_$$
+	path=/tmp/$name
+	if [ ! -w $SER_FIFO ]; then
+		echo "Error opening ser's FIFO $SER_FIFO"
+		echo "Make sure you have line fifo=$SER_FIFO in your config"
+		exit 1
+	fi
+	mkfifo $path
+	if [ $? -ne 0 ] ; then
+		echo "error opening read fifo $path"
+		exit 1
+	fi
+	chmod a+w $path
+
+	# construct the command now
+	CMD=":$1:$name\n";
+	shift
+	while [ -n "$1" ] ; do
+		CMD="${CMD}${1}\n"
+		shift
+	done
+	CMD="${CMD}\n"
+
+	trap "rm -f $path; exit 1" 2
+
+	# start reader now so that it is ready for replies
+	# immediately after a request was sent out
+	cat < $path | filter_fl &
+
+	# issue FIFO request (printf taken to deal with \n)
+	printf "$CMD" > $SER_FIFO
+
+	# wait for the reader to complete
+	wait
+	rm $path
+
+	if [ "0${FIFO_DBG}" -eq 1 ] ; then
+		printf "FIFO command was:\n$CMD"
+	fi
+}
+
+
+# $1 = name $2=path $3=attempt
+print_stats() {
+
+echo "[cycle #: $3; if constant make sure server lives and fifo is on]"
+
+cat < $2 | filter_fl &
+cat > $SER_FIFO <<EOF
+:version:$1
+
+EOF
+wait
+
+cat < $2 | filter_fl &
+cat > $SER_FIFO << EOF
+:uptime:$1
+
+EOF
+wait
+echo
+
+echo Transaction Statistics
+cat < $2 | filter_fl &
+cat > $SER_FIFO <<EOF
+:t_stats:$1
+
+EOF
+wait
+echo
+
+echo Stateless Server Statistics
+cat < $2 | filter_fl &
+cat > $SER_FIFO <<EOF
+:sl_stats:$1
+
+EOF
+wait
+echo
+
+echo UsrLoc Stats
+cat < $2 | filter_fl &
+cat > $SER_FIFO <<EOF
+:ul_stats:$1
+
+EOF
+wait
+}
+
+
+# input: sql query, optional mysql command-line params
+sql_query() {
+	# if password not yet queried, query it now
+	if [ -z "$PW" ] ; then
+		savetty=`stty -g`
+		printf "MySql password: "
+		stty -echo
+    	read PW >&2
+		stty $savetty
+    	echo >&2
+	fi
+	$PGSQL $2\
+		-A -q -t \
+		-P fieldsep="	" \
+		-h $SQL_HOST \
+		-U $SQL_USER \
+		$SQL_DB \
+		-c "$1"
+}
+
+# input: sql query, optional mysql command-line params
+sql_ro_query() {
+	$PGSQL $2\
+		-h $SQL_HOST \
+		-U $SQL_USER \
+		$SQL_DB \
+		-c "$1"
+}
+
+# input: sql query, optional mysql command-line params
+sql_ro_raw_query() {
+	$PGSQL $2\
+		-A -q -t \
+		-P fieldsep="	" \
+		-h $SQL_HOST \
+		-U $SQL_USER \
+		$SQL_DB \
+		-c "$1"
+}
+
+
+usrloc() {
+	if [ "$#" -lt 2 ] ; then
+		echo "usrloc: too few parameters"
+		exit 1
+	fi
+	if [ "$1" = "alias" ] ; then
+		TABLE="$A_TABLE"
+	elif [ "$1" = "ul" ] ; then
+		TABLE="$USRLOC"
+	else
+		echo "usrloc: unknown table name"
+		exit 1
+	fi
+	shift
+
+	case $1 in 
+		show)
+			if [ $# -eq 2 ] ; then
+				fifo_cmd ul_show_contact $TABLE $2
+			elif [ $# -eq 1 ] ; then
+				fifo_cmd ul_dump
+			else
+				echo "wrong number of params for usrloc show"
+				usage
+				exit 1
+			fi
+			exit $?
+			;;
+		add)
+			if [ $# -ne 3 ] ; then
+				usage
+				exit 1
+			fi
+			shift
+			check_uri "$2"
+			if [ "$?" -ne "0" ] ; then
+				echo "$2 is not a valid URI"
+				exit 1
+			fi
+
+			fifo_cmd ul_add "$TABLE" "$1" "$2" "$FOREVER_REL" "1.00" "0"
+			exit $?
+			;;
+		rm)
+            if [ $# -ne 2 ] ; then
+                usage
+                exit 1
+            fi
+			shift
+			fifo_cmd ul_rm $TABLE $1
+
+            ;;
+
+		*)
+			usage
+			exit 1
+			;;
+	esac
+}
+
+acl() {
+	case $1 in
+		show)
+			if [ $# -eq 2 ] ; then
+				is_user $2
+				if [ $? -ne 0 ] ; then
+					echo non-existent user
+					exit 1;
+				fi
+				CLAUSE=" WHERE $ACL_USER_COLUMN='$2' "
+			elif [ $# -ne 1 ] ; then
+				usage
+				exit 1
+			fi
+			QUERY="select * FROM $ACL_TABLE $CLAUSE ; "
+			sql_ro_query "$QUERY"
+
+			;;
+
+		grant)
+			if [ $# -lt 3 ] ; then
+				usage
+				exit 1
+			fi
+			prompt_pw
+			is_user $2
+			if [ $? -ne 0 ] ; then
+				echo non-existent user
+				exit 1
+			fi
+			SIP_USER="$2"
+			shift 2
+			while [ $# -gt 0 ] ; do
+
+				if [ $VERIFY_ACL -eq 1 ] ; then
+					found=0
+					for i in $ACL_GROUPS ; do
+						if [ "$1" = "$i" ] ; then
+							found=1
+							break
+						fi
+					done	
+					if [ $found -eq 0 ] ; then
+						echo "Invalid privilege: $1 ignored"
+						shift
+						continue
+					fi
+				fi
+
+        		QUERY="insert into $ACL_TABLE \
+                	($ACL_USER_COLUMN,$ACL_GROUP_COLUMN,$ACL_MODIFIED_COLUMN) \
+                	values ('$SIP_USER','$1', now());"
+				sql_query "$QUERY"
+				if [ $? -ne 0 ] ; then
+					echo "SQL Error"
+					exit 1
+				fi
+				shift
+			done
+
+			$0 acl show $SIP_USER
+
+			;;
+
+		revoke)
+			if [ $# -eq 3 ] ; then
+				CLAUSE=" and $ACL_GROUP_COLUMN='$3' "
+			elif [ $# -ne 2 ] ; then
+				usage
+				exit 1
+			fi	
+
+			QUERY="delete from $ACL_TABLE where \
+				$ACL_TABLE.$ACL_USER_COLUMN='$2' $CLAUSE"
+			sql_query "$QUERY"
+
+			$0 acl show $2
+
+			;;
+
+		*)
+			usage
+			exit 1
+			;;
+	esac
+}
+
+# params: user
+# output: false if exists, true otherwise
+is_user() {
+	QUERY="select count(*) from $TABLE \
+		where $SUBSCRIBER_COLUMN='$1' and $REALM_COLUMN='$SIP_DOMAIN';"
+	CNT=`sql_ro_raw_query "$QUERY" | grep -v ERROR | $LAST_LINE`
+	if [ "0$CNT" -eq 0 ] ; then
+		false
+	else
+		true
+	fi
+
+}
+
+
+# params: user, password
+# output: HA1, HA1B
+credentials()
+{
+	HA1=`$GENHA1 $1 $SIP_DOMAIN $2`
+	if [ $? -ne 0 ] ; then
+		echo "HA1 calculation failed"
+		exit 1
+	fi
+	HA1B=`$GENHA1 "$1@$SIP_DOMAIN" $SIP_DOMAIN $2`
+	if [ $? -ne 0 ] ; then
+		echo "HA1B calculation failed"
+		exit 1
+	fi
+}
+
+#================================================================
+
+if [ -z "$SIP_DOMAIN" ] ; then
+	echo
+	echo "You need to set environment variable SIP_DOMAIN (e.g. to 'foobar.com') first"
+	echo
+#	This confuses new users cause its easy to miss the information above
+#	usage
+	exit 1
+fi
+
+# if the script calls itself ...
+export PW
+
+case $1 in
+
+	start)
+		DIR=`dirname $0`
+		echo
+		printf "Starting SER : "
+		if [ -r $PID_FILE ] ; then
+			echo "PID file exists! ($PID_FILE) already running?"
+			exit 1
+		else
+			if [ $SYSLOG = 1 ] ; then
+				$DIR/ser -P $PID_FILE $STARTOPTIONS 1>/dev/null 2>/dev/null
+			else
+			 	$DIR/ser -P $PID_FILE -E $STARTOPTIONS
+			fi
+			sleep 1
+			echo "started pid(`cat $PID_FILE`)"
+		fi
+		exit 0
+	;;
+
+	stop)
+		printf "Stopping SER : "
+		if [ -r $PID_FILE ] ; then
+			kill `cat $PID_FILE`
+			echo "stopped"
+		else
+			echo No PID file found!
+			exit 1
+		fi
+		exit 0
+	;;
+
+	restart)
+	        DIR=`dirname $0`
+		printf "Stopping SER : "
+		if [ -r $PID_FILE ] ; then
+			kill `cat $PID_FILE`
+			echo "stopped"
+		else
+			echo No PID file found! SER problably not running
+			exit 1
+		fi
+		sleep 2
+		printf "Starting SER : "
+		if [ -r $PID_FILE ] ; then
+			echo "PID file exists! ($PID_FILE) already running?"
+			exit 1
+		else
+			if [ $SYSLOG = 1 ] ; then
+			        $DIR/ser -P $PID_FILE $STARTOPTIONS 1>/dev/null 2>/dev/null
+			else
+			        $DIR/ser -P $PID_FILE -E $STARTOPTIONS
+			fi
+			sleep 1
+			echo "started pid(`cat $PID_FILE`)"
+		fi
+		exit 0
+	;;
+
+	passwd)
+		if [ $# -ne 3 ] ; then
+			usage
+			exit 1
+		fi
+		shift
+		credentials $1 $2
+		prompt_pw
+
+		is_user $1
+		if [ $? -ne 0 ] ; then
+			echo non-existent user
+			exit 1
+		fi
+		QUERY="update $TABLE \
+			set $HA1_COLUMN='$HA1', $HA1B_COLUMN='$HA1B', $PASSWORD_COLUMN='$2' \
+			, $SUB_MODIFIED_COLUMN=now() \
+			WHERE $SUBSCRIBER_COLUMN='$1' and $REALM_COLUMN='$SIP_DOMAIN';"
+		sql_query "$QUERY"
+		if [ $? -ne 0 ] ; then
+			echo "password change failed"
+		else
+			echo "password change succeeded"
+		fi
+
+		;;		
+			
+		
+
+	add)
+		if [ $# -ne 4 ] ; then
+			usage
+			exit 1
+		fi
+		shift
+		credentials $1 $2
+		prompt_pw
+        is_user $1
+        if [ $? -eq 0 ] ; then
+            echo user already exists
+            exit 1
+        fi
+
+		QUERY="insert into $TABLE \
+				($SUBSCRIBER_COLUMN,$REALM_COLUMN,$HA1_COLUMN,\
+				$HA1B_COLUMN,$PASSWORD_COLUMN,$EMAIL_COLUMN, $SUB_CREATED_COLUMN,  \
+				$PHP_LIB_COLUMN,datetime_modified ) \
+				values ('$1','$SIP_DOMAIN','$HA1','$HA1B','$2', '$3', now(), '$HA1', now() );";
+		sql_query "$QUERY"
+		if [ $? -ne 0 ] ; then
+			echo "introducing a new user to the database failed"
+		else
+			echo "new user added"
+		fi
+
+		;;
+
+	monitor|console|moni|con)
+		name=ser_receiver_$$
+		path=/tmp/$name
+		if [ ! -w $SER_FIFO ]; then
+			echo "Error opening ser's FIFO $SER_FIFO"
+			echo "Make sure you have line fifo=$SER_FIFO in your config"
+			exit 1
+		fi
+		mkfifo $path
+		if [ $? -ne 0 ] ; then
+			echo "error opening read fifo $path"
+			exit 1
+		fi
+		trap "rm $path;  clear; echo sc monitor ^C-ed; exit 1" 2
+		attempt=0
+		clear
+		while [ 1 -eq 1 ]; do
+			attempt=`expr $attempt + 1`
+			#clear
+			tput cup 0 0
+			print_stats $name $path $attempt
+			sleep $WATCH_PERIOD
+		done
+		rm $path
+		exit 0
+		;;
+
+	mail)
+		if [ $# -ne 2 ] ; then
+			usage
+			exit 1
+		fi
+		shift
+		QUERY="select $TABLE.$EMAIL_COLUMN from $TABLE where  \
+			$TABLE.$SUBSCRIBER_COLUMN='$1'"
+		EA=`sql_ro_raw_query "$QUERY" "-B" | grep -v ERROR | $LAST_LINE`
+		if [ $? -ne 0 ] ; then
+			echo "MySql query failed"
+			exit 1
+		fi
+		echo "Write email to $1: $EA now ..."
+		mail -s "Message from $SIP_DOMAIN SIP admin" $EA
+		if [ $? -eq 0 ] ; then
+			echo message sent
+		else
+			echo sending message failed
+		fi
+
+		;;
+
+	alias|ul)
+		usrloc "$@"
+		;;
+
+	online)
+		fifo_cmd ul_dump |grep aor| awk '{print $3}' | sort | sort -mu
+		exit $?
+		;;
+
+
+	showdb|userdb)
+		if [ $# -eq 2 ] ; then
+			is_user $2
+			if [ $? -ne 0 ] ; then
+				echo non-existent user
+				exit 1;
+			fi
+		elif [ $# -ne 1  ] ; then
+			usage
+			exit 1
+		fi
+
+		shift
+
+		QUERY1="select $TABLE.$EMAIL_COLUMN from $TABLE where  \
+			$TABLE.$SUBSCRIBER_COLUMN='$1'"
+		QUERY2="select $USRLOC.* from $USRLOC where \
+			$USRLOC.$USER_COLUMN='$1' order by expires desc"
+		QUERY3="select $USRLOC.$USER_COLUMN, $TABLE.$EMAIL_COLUMN, $USRLOC.$CALLID_COLUMN \
+			from $TABLE, $USRLOC where  \
+			$TABLE.$SUBSCRIBER_COLUMN=$USRLOC.$USER_COLUMN  order by $USRLOC.$USER_COLUMN" 
+
+		if [ $# -eq 1 ] ; then
+			sql_ro_query "$QUERY1"
+			sql_ro_query "$QUERY2"
+		else
+			sql_ro_query "$QUERY3"
+		fi
+		echo "Note: Due to usage of cache, server's list " \
+			"may differ from DB list."
+
+		;;
+
+	rm)
+        if [ $# -ne 2 ] ; then
+            usage
+            exit 1
+        fi
+		shift 
+		prompt_pw 
+
+        is_user $1
+        if [ $? -ne 0 ] ; then
+            echo non-existent user
+            exit 1
+        fi
+
+        QUERY="delete from $TABLE where $TABLE.$SUBSCRIBER_COLUMN='$1'"
+		sql_query "$QUERY"
+
+		$0 acl revoke $1 > /dev/null 2>&1
+		$0 dul $1   > /dev/null 2>&1
+        ;;
+			
+	ps)
+		fifo_cmd ps
+		;;
+
+	acl)
+		shift
+		acl "$@"
+		;;
+
+	fifo)
+		shift
+		fifo_cmd "$@"
+		;;
+
+	version)
+		echo  "$0 $VERSION"
+		;;
+		
+	*)
+		usage
+		exit 1
+		;;
+
+esac
+