--- chan_iax2.c	2004-03-03 16:21:34.000000000 -0600
+++ chan_iax25.c	2004-03-05 17:04:12.000000000 -0600
@@ -60,6 +60,9 @@
 #ifdef MYSQL_FRIENDS
 #include <mysql/mysql.h>
 #endif
+#ifdef PGSQL_FRIENDS
+#include <libpq-fe.h>
+#endif
 #include "iax2.h"
 #include "iax2-parser.h"
 #include "../astconf.h"
@@ -101,6 +104,18 @@
 static char mydbhost[80];
 static char mydbname[80];
 #endif
+
+#ifdef PGSQL_FRIENDS
+static ast_mutex_t pgsqllock = AST_MUTEX_INITIALIZER;
+PGconn	   *pgdbconn;
+PGresult   *pgdbresult;
+static char pgdbuser[80];
+static char pgdbpass[80];
+static char pgdbhost[80];
+static char pgdbname[80];
+static char pgdbport[80];
+#endif
+
 static char *desc = "Inter Asterisk eXchange (Ver 2)";
 static char *tdesc = "Inter Asterisk eXchange Driver (Ver 2)";
 static char *type = "IAX2";
@@ -1762,6 +1777,200 @@
 	return 0;
 }
 
+#ifdef PGSQL_FRIENDS
+
+static void pgsql_update_peer(char *peer, struct sockaddr_in *sin)
+{
+	if (pgdbconn && ((int)pgdbresult != CONNECTION_BAD) && (strlen(peer) < 128)) {
+		char query[512];
+		char *name;
+		time_t nowtime;
+		name = alloca(strlen(peer) * 2 + 1);
+		time(&nowtime);
+		
+		// Clean up the peer if necessary
+		PQescapeString(name, peer, strlen(peer));
+		
+		//mysql_real_escape_string(mysql, name, peer, strlen(peer));
+		snprintf(query, sizeof(query), "UPDATE iaxfriends SET ipaddr='%s', port=%d, regseconds=%ld WHERE name='%s'", 
+			inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), nowtime, name);
+		ast_mutex_lock(&pgsqllock);
+		if (PQresultStatus(PQexec(pgdbconn, query)) != PGRES_COMMAND_OK)
+			ast_log(LOG_WARNING, "Unable to update database\n");
+			
+		ast_mutex_unlock(&pgsqllock);
+	}
+}
+
+static struct iax2_peer *pgsql_peer(char *peer)
+{
+	struct iax2_peer *p;
+	int success = 0;
+	
+	p = malloc(sizeof(struct iax2_peer));
+	memset(p, 0, sizeof(struct iax2_peer));
+	
+	if (pgdbconn && ((int)pgdbresult != CONNECTION_BAD) && (strlen(peer) < 128)) {
+		
+		char query[512];
+		char value[128];
+		char fname[128];
+		char *name;
+		PGresult *result;
+		int port, j, nFields;
+		
+		time_t regseconds, nowtime;
+		name = alloca(strlen(peer) * 2 + 1);
+		
+		// Clean up the peer name (arg: peer) and place in 'name'
+		PQescapeString(name, peer, strlen(peer));
+		// Generate the query from the peer name passed in...
+		snprintf(query, sizeof(query), "SELECT * FROM iaxfriends WHERE name='%s'", name);		
+		// Lock this section to prevent reentrance
+		ast_mutex_lock(&pgsqllock);
+		// Execute the query
+		result = PQexec(pgdbconn, query);
+		// Unable To Query Database or no records returned - Fail
+		if (!result || (PQresultStatus(result) != PGRES_TUPLES_OK))
+		{
+			ast_mutex_unlock(&pgsqllock);
+			free(p);
+			return NULL;
+		}
+		// Query worked correctly - Look for valid data
+		success = 1;
+		nFields = PQnfields(result);
+		
+		// Walk the returned fields - we only read the first row
+		for (j = 0; j < nFields; j++) {
+			strcpy(value, PQgetvalue(result, 0, j));
+			strcpy(fname, PQfname(result, j));
+			if (value) {
+				if (!strcasecmp(fname, "secret")) {
+					strncpy(p->secret, value, sizeof(p->secret));
+				} else if (!strcasecmp(fname, "context")) {
+					strncpy(p->context, value, sizeof(p->context) - 1);
+				} else if (!strcasecmp(fname, "ipaddr")) {
+					inet_aton(value, &p->addr.sin_addr);
+				} else if (!strcasecmp(fname, "port")) {
+					if (sscanf(value, "%i", &port) != 1)
+						port = 0;
+					p->addr.sin_port = htons(port);
+				} else if (!strcasecmp(value, "regseconds")) {
+					if (sscanf(value, "%li", &regseconds) != 1)
+						regseconds = 0;
+				}
+			}
+		}
+		
+		time(&nowtime);
+		if ((nowtime - regseconds) > IAX_DEFAULT_REG_EXPIRE) 
+			memset(&p->addr, 0, sizeof(p->addr));
+		
+		// release the result
+		PQclear(result);
+		result = NULL;
+	
+		// unlock the section
+		ast_mutex_unlock(&pgsqllock);
+	}
+
+	if (!success) {
+		free(p);
+		p = NULL;
+	} else {
+		strncpy(p->name, peer, sizeof(p->name) - 1);
+		p->dynamic = 1;
+		p->temponly = 1;
+		p->expire = -1;
+		p->capability = iax2_capability;
+		p->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
+	}
+	return p;
+}
+
+static struct iax2_user *pgsql_user(char *user)
+{
+	struct iax2_user *p;
+	struct iax2_context *con;
+	int success = 0;
+	
+	p = malloc(sizeof(struct iax2_user));
+	memset(p, 0, sizeof(struct iax2_user));
+	con = malloc(sizeof(struct iax2_context));
+	memset(con, 0, sizeof(struct iax2_context));
+	strcpy(con->context, "default");
+	p->contexts = con;
+	if (pgdbconn && ((int)pgdbresult != CONNECTION_BAD) && (strlen(user) < 128)) {
+		char query[512];
+		char value[128];
+		char fname[128];
+		char *name;
+		int j, nFields;
+		PGresult *result;
+		
+		// Allocate space for the escaped name
+		name = alloca(strlen(user) * 2 + 1);
+		
+		// Clean up the peer name (arg: peer) and place in 'name'
+		PQescapeString(name, user, strlen(user));
+		
+		// Format the query
+		snprintf(query, sizeof(query), "SELECT * FROM iaxfriends WHERE name='%s'", name);
+		
+		// Lock the section
+		ast_mutex_lock(&pgsqllock);
+		
+		// Execute the query
+		result = PQexec(pgdbconn, query);
+
+		// Bail out if not found or failed
+		if (!result || (PQresultStatus(result) != PGRES_TUPLES_OK))
+		{
+			ast_mutex_unlock(&pgsqllock);
+			free(p);
+			return NULL;
+		}
+		
+		// Query worked correctly - Look for valid data
+		success = 1;
+		nFields = PQnfields(result);
+		
+		// Walk the returned fields - we only read the first row
+		for (j = 0; j < nFields; j++) {
+			strcpy(value, PQgetvalue(result, 0, j));
+			strcpy(fname, PQfname(result, j));
+
+			if (value) {
+				if (!strcasecmp(fname, "secret")) {
+					strncpy(p->secret, value, sizeof(p->secret));
+				} else if (!strcasecmp(fname, "context")) {
+					strncpy(p->contexts->context, value, sizeof(p->contexts->context) - 1);
+				}
+			}
+		}
+		
+		PQclear(result);
+		result = NULL;
+
+		ast_mutex_unlock(&pgsqllock);
+	}
+	if (!success) {
+		if (p->contexts)
+			free(p->contexts);
+		free(p);
+		p = NULL;
+	} else {
+		strncpy(p->name, user, sizeof(p->name) - 1);
+		p->temponly = 1;
+		p->capability = iax2_capability;
+		p->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
+	}
+	return p;
+}
+#endif
+
+//MYSQL DATABASE INTEGRATION FUNCTIONS
 #ifdef MYSQL_FRIENDS
 
 static void mysql_update_peer(char *peer, struct sockaddr_in *sin)
@@ -1932,7 +2141,13 @@
 #ifdef MYSQL_FRIENDS
 	if (!p)
 		p = mysql_peer(peer);
-#endif		
+#endif	
+
+#ifdef PGSQL_FRIENDS
+	if (!p)
+		p = pgsql_peer(peer);
+#endif
+
 	if (p) {
 		found++;
 		if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) &&
@@ -3193,6 +3408,20 @@
 		}
 	}
 #endif	
+
+#ifdef PGSQL_FRIENDS
+	if (!user && pgdbconn && strlen(iaxs[callno]->username) && (strlen(iaxs[callno]->username) < 128)) {
+		user = pgsql_user(iaxs[callno]->username);
+		if (user && strlen(iaxs[callno]->context) &&			/* No context specified */
+			     !apply_context(user->contexts, iaxs[callno]->context)) {			/* Context is permitted */
+			if (user->contexts)
+				free(user->contexts);
+			free(user);
+			user = NULL;
+		}
+	}
+#endif
+
 	if (user) {
 		/* We found our match (use the first) */
 		
@@ -3356,7 +3585,10 @@
 	if (!p) 
 		p = mysql_peer(peer);
 #endif
-
+#ifdef PGSQL_FRIENDS
+	if (!p)
+		p = pgsql_peer(peer);
+#endif
 	if (!p) {
 		if (authdebug)
 			ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, inet_ntoa(sin->sin_addr));
@@ -3879,12 +4111,20 @@
 #ifdef MYSQL_FRIENDS
 	if (!p)
 		p = mysql_peer(name);
+#endif
+#ifdef PGSQL_FRIENDS
+	if (!p)
+		p = pgsql_peer(name);
 #endif	
 	if (p) {
 #ifdef MYSQL_FRIENDS
 		if (p->temponly)
 			mysql_update_peer(name, sin);
 #endif
+#ifdef PGSQL_FRIENDS
+		if (p->temponly)
+			pgsql_update_peer(name, sin);
+#endif
 		if (inaddrcmp(&p->addr, sin)) {
 			if (iax2_regfunk)
 				iax2_regfunk(p->name, 1);
@@ -3952,7 +4192,11 @@
 #ifdef MYSQL_FRIENDS
 	if (!p)
 		p = mysql_peer(name);
-#endif			
+#endif
+#ifdef PGSQL_FRIENDS
+	if (!p)
+		p = pgsql_peer(name);
+#endif		
 	if (p) {
 		memset(&ied, 0, sizeof(ied));
 		iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
@@ -5982,6 +6226,18 @@
 		} else if (!strcasecmp(v->name, "dbname")) {
 			strncpy(mydbname, v->value, sizeof(mydbname) - 1);
 #endif
+#ifdef PGSQL_FRIENDS
+		} else if (!strcasecmp(v->name, "dbuser")) {
+			strncpy(pgdbuser, v->value, sizeof(pgdbuser) - 1);
+		} else if (!strcasecmp(v->name, "dbpass")) {
+			strncpy(pgdbpass, v->value, sizeof(pgdbpass) - 1);
+		} else if (!strcasecmp(v->name, "dbhost")) {
+			strncpy(pgdbhost, v->value, sizeof(pgdbhost) - 1);
+		} else if (!strcasecmp(v->name, "dbname")) {
+			strncpy(pgdbname, v->value, sizeof(pgdbname) - 1);
+		} else if (!strcasecmp(v->name, "dbport")) {
+			strncpy(pgdbport, v->value, sizeof(pgdbport) - 1);			
+#endif
 		} //else if (strcasecmp(v->name,"type"))
 		//	ast_log(LOG_WARNING, "Ignoring %s\n", v->name);
 		v = v->next;
@@ -6034,6 +6290,24 @@
 				mydbname, mydbhost, mydbuser);
 	}
 #endif
+
+#ifdef PGSQL_FRIENDS
+	/* Connect to db if appropriate */
+	if (!pgdbconn && strlen(pgdbname)) {
+		pgdbconn = PQsetdbLogin(pgdbhost, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgdbpass);
+		if (PQstatus(pgdbconn) == CONNECTION_BAD) {
+			memset(pgdbpass, '*', strlen(pgdbpass));
+			ast_log(LOG_WARNING, "Database connection failed (db=%s, host=%s, user=%s, pass=%s)!\n",
+				pgdbname, pgdbhost, pgdbuser, pgdbpass);
+			ast_log(LOG_WARNING, "Error: %s\n",
+				PQerrorMessage(pgdbconn));				
+			pgdbconn = NULL;
+		} else
+			ast_verbose(VERBOSE_PREFIX_1 "Connected to database '%s' on '%s' as '%s'\n",
+				pgdbname, pgdbhost, pgdbuser);
+	}
+#endif
+
 	return capability;
 }
 
