From ebb18daf223b7c8bcf226d17b87bfa0cc9b3c112 Mon Sep 17 00:00:00 2001
From: Mark Spencer <markster@digium.com>
Date: Tue, 5 Oct 2004 06:46:11 +0000
Subject: [PATCH] Major changes to res_config to support centralized config,
 eliminate configuration of res_config_odbc, update config examples, integrate
 with iax2, remove mysql friends from iax2, put on flame retardant vest...

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@3914 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 apps/app_queue.c                    |  74 ++-
 asterisk.c                          |   3 +
 channels/chan_iax2.c                | 381 ++++++----------
 config.c                            | 673 +++++++---------------------
 configs/extconfig.conf.sample       |  28 +-
 configs/res_config_odbc.conf.sample |   3 -
 doc/README.extconfig                |  38 ++
 include/asterisk/config.h           |  36 +-
 include/asterisk/config_pvt.h       |  29 +-
 include/asterisk/res_odbc.h         |   2 +-
 include/asterisk/utils.h            |   2 +
 res/res_config_odbc.c               | 269 +++++++++--
 res/res_odbc.c                      |  43 +-
 utils.c                             |  13 +
 14 files changed, 744 insertions(+), 850 deletions(-)
 delete mode 100755 configs/res_config_odbc.conf.sample
 create mode 100755 doc/README.extconfig

diff --git a/apps/app_queue.c b/apps/app_queue.c
index 24bea7492b..0febe56221 100755
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -730,6 +730,7 @@ static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser
 	int numlines;
 	int sentringing = 0;
 	int numbusies = 0;
+	int numnochan = 0;
 	int orig = *to;
 	struct ast_frame *f;
 	struct localuser *peer = NULL;
@@ -754,7 +755,7 @@ static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser
 			numlines++;
 		}
 		if (found < 0) {
-			if (numlines == numbusies) {
+			if (numlines == (numbusies + numnochan)) {
 				ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
 			} else {
 				ast_log(LOG_NOTICE, "No one is answering queue '%s'\n", queue);
@@ -776,6 +777,77 @@ static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser
 					*allowdisconnect_out = o->allowdisconnect_out;
 				}
 			} else if (o->chan && (o->chan == winner)) {
+				if (!ast_strlen_zero(o->chan->call_forward)) {
+					char tmpchan[256]="";
+					char *stuff;
+					char *tech;
+					strncpy(tmpchan, o->chan->call_forward, sizeof(tmpchan) - 1);
+					if ((stuff = strchr(tmpchan, '/'))) {
+						*stuff = '\0';
+						stuff++;
+						tech = tmpchan;
+					} else {
+						snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
+						stuff = tmpchan;
+						tech = "Local";
+					}
+					/* Before processing channel, go ahead and check for forwarding */
+					if (option_verbose > 2)
+						ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
+					/* Setup parameters */
+					o->chan = ast_request(tech, in->nativeformats, stuff);
+					if (!o->chan) {
+						ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
+						o->stillgoing = 0;
+						numnochan++;
+					} else {
+						if (o->chan->cid.cid_num)
+							free(o->chan->cid.cid_num);
+						o->chan->cid.cid_num = NULL;
+						if (o->chan->cid.cid_name)
+							free(o->chan->cid.cid_name);
+						o->chan->cid.cid_name = NULL;
+
+						if (in->cid.cid_num) {
+							o->chan->cid.cid_num = strdup(in->cid.cid_num);
+							if (!o->chan->cid.cid_num)
+								ast_log(LOG_WARNING, "Out of memory\n");	
+						}
+						if (in->cid.cid_name) {
+							o->chan->cid.cid_name = strdup(in->cid.cid_name);
+							if (!o->chan->cid.cid_name)
+								ast_log(LOG_WARNING, "Out of memory\n");	
+						}
+						strncpy(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode) - 1);
+						o->chan->cdrflags = in->cdrflags;
+
+						if (in->cid.cid_ani) {
+							if (o->chan->cid.cid_ani)
+								free(o->chan->cid.cid_ani);
+							o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
+							if (o->chan->cid.cid_ani)
+								strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
+							else
+								ast_log(LOG_WARNING, "Out of memory\n");
+						}
+						if (o->chan->cid.cid_rdnis) 
+							free(o->chan->cid.cid_rdnis);
+						if (!ast_strlen_zero(in->macroexten))
+							o->chan->cid.cid_rdnis = strdup(in->macroexten);
+						else
+							o->chan->cid.cid_rdnis = strdup(in->exten);
+						if (ast_call(o->chan, tmpchan, 0)) {
+							ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
+							o->stillgoing = 0;
+							ast_hangup(o->chan);
+							o->chan = NULL;
+							numnochan++;
+						}
+					}
+					/* Hangup the original channel now, in case we needed it */
+					ast_hangup(winner);
+					continue;
+				}
 				f = ast_read(winner);
 				if (f) {
 					if (f->frametype == AST_FRAME_CONTROL) {
diff --git a/asterisk.c b/asterisk.c
index cbf9ca0176..1a0a6dfe3a 100755
--- a/asterisk.c
+++ b/asterisk.c
@@ -1859,12 +1859,15 @@ int main(int argc, char *argv[])
 		printf(term_quit());
 		exit(1);
 	}
+#if 0
+	/* This should no longer be necessary */
 	/* sync cust config and reload some internals in case a custom config handler binded to them */
 	read_ast_cust_config();
 	reload_logger(0);
 	reload_manager();
 	ast_enum_reload();
 	ast_rtp_reload();
+#endif
 
 
 	/* We might have the option of showing a console, but for now just
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index c495f53d9f..8024be534a 100755
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -62,9 +62,6 @@
 #include <zaptel.h>
 #endif /* __linux__ */
 #endif
-#ifdef MYSQL_FRIENDS
-#include <mysql/mysql.h>
-#endif
 #include "iax2.h"
 #include "iax2-parser.h"
 #include "iax2-provision.h"
@@ -99,14 +96,6 @@
 /* Sample over last 100 units to determine historic jitter */
 #define GAMMA (0.01)
 
-#ifdef MYSQL_FRIENDS
-AST_MUTEX_DEFINE_STATIC(mysqllock);
-static MYSQL *mysql;
-static char mydbuser[80];
-static char mydbpass[80];
-static char mydbhost[80];
-static char mydbname[80];
-#endif
 static char *desc = "Inter Asterisk eXchange (Ver 2)";
 static char *tdesc = "Inter Asterisk eXchange Driver (Ver 2)";
 static char *type = "IAX2";
@@ -1990,154 +1979,108 @@ static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newcha
 	return 0;
 }
 
-#ifdef MYSQL_FRIENDS
-
-static void mysql_update_peer(char *peer, struct sockaddr_in *sin)
-{
-	if (mysql && (strlen(peer) < 128)) {
-		char query[512];
-		char *name;
-		char iabuf[INET_ADDRSTRLEN];
-		time_t nowtime;
-		name = alloca(strlen(peer) * 2 + 1);
-		time(&nowtime);
-		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\"", 
-			ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port), nowtime, name);
-		ast_mutex_lock(&mysqllock);
-		if (mysql_real_query(mysql, query, strlen(query))) 
-			ast_log(LOG_WARNING, "Unable to update database\n");
-			
-		ast_mutex_unlock(&mysqllock);
-	}
-}
-
-static struct iax2_peer *mysql_peer(char *peer)
-{
-	struct iax2_peer *p;
-	int success = 0;
-	
-	p = malloc(sizeof(struct iax2_peer));
-	memset(p, 0, sizeof(struct iax2_peer));
-	if (mysql && (strlen(peer) < 128)) {
-		char query[512];
-		char *name;
-		int numfields, x;
-		int port;
-		time_t regseconds, nowtime;
-		MYSQL_RES *result;
-		MYSQL_FIELD *fields;
-		MYSQL_ROW rowval;
-		name = alloca(strlen(peer) * 2 + 1);
-		mysql_real_escape_string(mysql, name, peer, strlen(peer));
-		snprintf(query, sizeof(query), "SELECT name, secret, context, ipaddr, port, regseconds FROM iaxfriends WHERE name=\"%s\"", name);
-		ast_mutex_lock(&mysqllock);
-		mysql_query(mysql, query);
-		if ((result = mysql_store_result(mysql))) {
-			if ((rowval = mysql_fetch_row(result))) {
-				numfields = mysql_num_fields(result);
-				fields = mysql_fetch_fields(result);
-				success = 1;
-				for (x=0;x<numfields;x++) {
-					if (rowval[x]) {
-						if (!strcasecmp(fields[x].name, "secret")) {
-							strncpy(p->secret, rowval[x], sizeof(p->secret) - 1);
-						} else if (!strcasecmp(fields[x].name, "context")) {
-							strncpy(p->context, rowval[x], sizeof(p->context) - 1);
-						} else if (!strcasecmp(fields[x].name, "ipaddr")) {
-							inet_aton(rowval[x], &p->addr.sin_addr);
-						} else if (!strcasecmp(fields[x].name, "port")) {
-							if (sscanf(rowval[x], "%i", &port) != 1)
-								port = 0;
-							p->addr.sin_port = htons(port);
-						} else if (!strcasecmp(fields[x].name, "regseconds")) {
-							if (sscanf(rowval[x], "%li", &regseconds) != 1)
-								regseconds = 0;
-						}
-					}
+static struct iax2_peer *build_peer(const char *name, struct ast_variable *v);
+static struct iax2_user *build_user(const char *name, struct ast_variable *v);
+
+static void destroy_user(struct iax2_user *user);
+static void destroy_peer(struct iax2_peer *peer);
+
+static struct iax2_peer *realtime_peer(const char *peername)
+{
+	struct ast_variable *var;
+	struct ast_variable *tmp;
+	struct iax2_peer *peer=NULL;
+	time_t regseconds, nowtime;
+	int dynamic=0;
+	var = ast_load_realtime("iaxfriends", "name", peername);
+	if (var) {
+		/* Make sure it's not a user only... */
+		peer = build_peer(peername, var);
+		if (peer) {
+			/* Add some finishing touches, addresses, etc */
+			peer->temponly = 1;
+			tmp = var;
+			while(tmp) {
+				if (!strcasecmp(tmp->name, "type")) {
+					if (strcasecmp(tmp->value, "friend") &&
+						strcasecmp(tmp->value, "peer")) {
+						/* Whoops, we weren't supposed to exist! */
+						destroy_peer(peer);
+						peer = NULL;
+						break;
+					} 
+				} else if (!strcasecmp(tmp->name, "regseconds")) {
+					if (sscanf(tmp->value, "%li", &regseconds) != 1)
+						regseconds = 0;
+				} else if (!strcasecmp(tmp->name, "ipaddr")) {
+					inet_aton(tmp->value, &(peer->addr.sin_addr));
+				} else if (!strcasecmp(tmp->name, "port")) {
+					peer->addr.sin_port = htons(atoi(tmp->value));
+				} else if (!strcasecmp(tmp->name, "host")) {
+					if (!strcasecmp(tmp->value, "dynamic"))
+						dynamic = 1;
 				}
+				tmp = tmp->next;
+			}
+			if (peer && dynamic) {
 				time(&nowtime);
-				if ((nowtime - regseconds) > IAX_DEFAULT_REG_EXPIRE) 
-					memset(&p->addr, 0, sizeof(p->addr));
+				if ((nowtime - regseconds) > IAX_DEFAULT_REG_EXPIRE) {
+					memset(&peer->addr, 0, sizeof(peer->addr));
+					if (option_debug)
+						ast_log(LOG_DEBUG, "Bah, we're expired (%ld/%ld/%ld)!\n", nowtime - regseconds, regseconds, nowtime);
+				}
 			}
-			mysql_free_result(result);
-			result = NULL;
 		}
-		ast_mutex_unlock(&mysqllock);
+		ast_destroy_realtime(var);
 	}
-	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;
+	return peer;
 }
-static struct iax2_user *mysql_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));
-	strncpy(con->context, "default", sizeof(con->context) - 1);
-	p->contexts = con;
-	if (mysql && (strlen(user) < 128)) {
-		char query[512];
-		char *name;
-		int numfields, x;
-		MYSQL_RES *result;
-		MYSQL_FIELD *fields;
-		MYSQL_ROW rowval;
-		name = alloca(strlen(user) * 2 + 1);
-		mysql_real_escape_string(mysql, name, user, strlen(user));
-		snprintf(query, sizeof(query), "SELECT name, secret, context, ipaddr, port, regseconds, accountcode FROM iaxfriends WHERE name=\"%s\"", name);
-		ast_mutex_lock(&mysqllock);
-		mysql_query(mysql, query);
-		if ((result = mysql_store_result(mysql))) {
-			if ((rowval = mysql_fetch_row(result))) {
-				numfields = mysql_num_fields(result);
-				fields = mysql_fetch_fields(result);
-				success = 1;
-				for (x=0;x<numfields;x++) {
-					if (rowval[x]) {
-						if (!strcasecmp(fields[x].name, "secret")) {
-							strncpy(p->secret, rowval[x], sizeof(p->secret) - 1);
-						} else if (!strcasecmp(fields[x].name, "context")) {
-							strncpy(p->contexts->context, rowval[x], sizeof(p->contexts->context) - 1);
-						} else if (!strcasecmp(fields[x].name, "accountcode")) {
-							strncpy(p->accountcode, rowval[x], sizeof(p->accountcode) - 1);
-						}
-					}
+
+static struct iax2_user *realtime_user(const char *username)
+{
+	struct ast_variable *var;
+	struct ast_variable *tmp;
+	struct iax2_user *user=NULL;
+	var = ast_load_realtime("iaxfriends", "name", username);
+	if (var) {
+		/* Make sure it's not a user only... */
+		user = build_user(username, var);
+		if (user) {
+			/* Add some finishing touches, addresses, etc */
+			user->temponly = 1;
+			tmp = var;
+			while(tmp) {
+				if (!strcasecmp(tmp->name, "type")) {
+					if (strcasecmp(tmp->value, "friend") &&
+						strcasecmp(tmp->value, "user")) {
+						/* Whoops, we weren't supposed to exist! */
+						destroy_user(user);
+						user = NULL;
+						break;
+					} 
 				}
+				tmp = tmp->next;
 			}
-			mysql_free_result(result);
-			result = NULL;
 		}
-		ast_mutex_unlock(&mysqllock);
+		ast_destroy_realtime(var);
 	}
-	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;
+	return user;
+}
+
+static void realtime_update(const char *peername, struct sockaddr_in *sin)
+{
+	char port[10];
+	char ipaddr[20];
+	char regseconds[20];
+	time_t nowtime;
+	
+	time(&nowtime);
+	snprintf(regseconds, sizeof(regseconds), "%ld", nowtime);
+	ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin->sin_addr);
+	snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
+	ast_update_realtime("iaxfriends", "name", peername, "ipaddr", ipaddr, "port", port, "regseconds", regseconds, NULL);
 }
-#endif /* MYSQL_FRIENDS */
+
 
 static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, int *maxtime, char *peer, char *context, int *trunk, int *notransfer, int *usejitterbuf, char *username, int usernlen, char *secret, int seclen, int *ofound, char *peercontext)
 {
@@ -2160,10 +2103,8 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, i
 		p = p->next;
 	}
 	ast_mutex_unlock(&peerl.lock);
-#ifdef MYSQL_FRIENDS
 	if (!p)
-		p = mysql_peer(peer);
-#endif		
+		p = realtime_peer(peer);
 	if (p) {
 		found++;
 		if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) &&
@@ -2197,7 +2138,7 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, i
 				*usejitterbuf=p->usejitterbuf;
 		} else {
 			if (p->temponly)
-				free(p);
+				destroy_peer(p);
 			p = NULL;
 		}
 	}
@@ -2216,7 +2157,7 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, i
 	} else if (!p)
 		return -1;
 	if (p->temponly)
-		free(p);
+		destroy_peer(p);
 	return 0;
 }
 
@@ -3691,18 +3632,14 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
 	}
 	ast_mutex_unlock(&userl.lock);
 	user = best;
-#ifdef MYSQL_FRIENDS
-	if (!user && mysql && !ast_strlen_zero(iaxs[callno]->username) && (strlen(iaxs[callno]->username) < 128)) {
-		user = mysql_user(iaxs[callno]->username);
+	if (!user && !ast_strlen_zero(iaxs[callno]->username) && (strlen(iaxs[callno]->username) < 128)) {
+		user = realtime_user(iaxs[callno]->username);
 		if (user && !ast_strlen_zero(iaxs[callno]->context) &&			/* No context specified */
 			     !apply_context(user->contexts, iaxs[callno]->context)) {			/* Context is permitted */
-			if (user->contexts)
-				free(user->contexts);
-			free(user);
+			destroy_user(user);
 			user = NULL;
 		}
 	}
-#endif	
 	if (user) {
 		/* We found our match (use the first) */
 		
@@ -3874,10 +3811,8 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 		if (!strcasecmp(p->name, peer))
 			break;
 	ast_mutex_unlock(&peerl.lock);
-#ifdef MYSQL_FRIENDS
 	if (!p) 
-		p = mysql_peer(peer);
-#endif
+		p = realtime_peer(peer);
 
 	if (!p) {
 		if (authdebug)
@@ -3889,7 +3824,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 		if (authdebug)
 			ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr));
 		if (p->temponly)
-			free(p);
+			destroy_peer(p);
 		return -1;
 	}
 
@@ -3897,7 +3832,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 		if (authdebug)
 			ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), p->name);
 		if (p->temponly)
-			free(p);
+			destroy_peer(p);
 		return -1;
 	}
 	strncpy(iaxs[callno]->secret, p->secret, sizeof(iaxs[callno]->secret)-1);
@@ -3923,14 +3858,14 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 				if (authdebug)
 					ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys);
 				if (p->temponly)
-					free(p);
+					destroy_peer(p);
 				return -1;
 			}
 		} else {
 			if (authdebug)
 				ast_log(LOG_NOTICE, "Host '%s' trying to do RSA authentication, but we have no inkeys\n", peer);
 			if (p->temponly)
-				free(p);
+				destroy_peer(p);
 			return -1;
 		}
 	} else if (!ast_strlen_zero(secret) && (p->authmethods & IAX_AUTH_PLAINTEXT)) {
@@ -3939,7 +3874,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 			if (authdebug)
 				ast_log(LOG_NOTICE, "Host %s did not provide proper plaintext password for '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), p->name);
 			if (p->temponly)
-				free(p);
+				destroy_peer(p);
 			return -1;
 		} else
 			iaxs[callno]->state |= IAX_STATE_AUTHENTICATED;
@@ -3956,7 +3891,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 			if (authdebug)
 				ast_log(LOG_NOTICE, "Host %s failed MD5 authentication for '%s' (%s != %s)\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), p->name, requeststr, md5secret);
 			if (p->temponly)
-				free(p);
+				destroy_peer(p);
 			return -1;
 		} else
 			iaxs[callno]->state |= IAX_STATE_AUTHENTICATED;
@@ -3964,7 +3899,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 		if (authdebug)
 			ast_log(LOG_NOTICE, "Inappropriate authentication received\n");
 		if (p->temponly)
-			free(p);
+			destroy_peer(p);
 		return -1;
 	}
 	strncpy(iaxs[callno]->peer, peer, sizeof(iaxs[callno]->peer)-1);
@@ -3972,7 +3907,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 	if (expire && (expire < iaxs[callno]->expirey)) 
 		iaxs[callno]->expirey = expire;
 	if (p->temponly)
-		free(p);
+		destroy_peer(p);
 	return 0;
 	
 }
@@ -4342,9 +4277,10 @@ static void register_peer_exten(struct iax2_peer *peer, int onoff)
 		strncpy(multi, ast_strlen_zero(peer->regexten) ? peer->name : peer->regexten, sizeof(multi) - 1);
 		stringp = multi;
 		while((ext = strsep(&stringp, "&"))) {
-			if (onoff)
-				ast_add_extension(regcontext, 1, ext, 1, NULL, NULL, "Noop", strdup(peer->name), free, type);
-			else
+			if (onoff) {
+				if (!ast_exists_extension(NULL, regcontext, ext, 1, NULL))
+					ast_add_extension(regcontext, 1, ext, 1, NULL, NULL, "Noop", strdup(peer->name), free, type);
+			} else
 				ast_context_remove_extension(regcontext, ext, 1, NULL);
 		}
 	}
@@ -4422,15 +4358,11 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno, char
 			break;
 		}
 	}
-#ifdef MYSQL_FRIENDS
 	if (!p)
-		p = mysql_peer(name);
-#endif	
+		p = realtime_peer(name);
 	if (p) {
-#ifdef MYSQL_FRIENDS
 		if (p->temponly)
-			mysql_update_peer(name, sin);
-#endif
+			realtime_update(name, sin);
 		if (inaddrcmp(&p->addr, sin)) {
 			if (iax2_regfunk)
 				iax2_regfunk(p->name, 1);
@@ -4490,7 +4422,7 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno, char
 		if (version) 
 			iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version);
 		if (p->temponly)
-			free(p);
+			destroy_peer(p);
 		return send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
 	}
 	ast_log(LOG_WARNING, "No such peer '%s'\n", name);
@@ -4508,10 +4440,8 @@ static int registry_authrequest(char *name, int callno)
 		}
 	}
 	ast_mutex_unlock(&peerl.lock);
-#ifdef MYSQL_FRIENDS
 	if (!p)
-		p = mysql_peer(name);
-#endif			
+		p = realtime_peer(name);
 	if (p) {
 		memset(&ied, 0, sizeof(ied));
 		iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
@@ -4522,7 +4452,7 @@ static int registry_authrequest(char *name, int callno)
 		}
 		iax_ie_append_str(&ied, IAX_IE_USERNAME, name);
 		if (p->temponly)
-			free(p);
+			destroy_peer(p);
 		return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
 	} 
 	ast_log(LOG_WARNING, "No such peer '%s'\n", name);
@@ -6375,7 +6305,7 @@ static int get_auth_methods(char *value)
 	return methods;
 }
 
-static struct iax2_peer *build_peer(char *name, struct ast_variable *v)
+static struct iax2_peer *build_peer(const char *name, struct ast_variable *v)
 {
 	struct iax2_peer *peer;
 	struct iax2_peer *prev;
@@ -6540,7 +6470,7 @@ static struct iax2_peer *build_peer(char *name, struct ast_variable *v)
 	return peer;
 }
 
-static struct iax2_user *build_user(char *name, struct ast_variable *v)
+static struct iax2_user *build_user(const char *name, struct ast_variable *v)
 {
 	struct iax2_user *prev, *user;
 	struct iax2_context *con, *conl = NULL;
@@ -6702,6 +6632,13 @@ static void delete_users(void)
 	ast_mutex_unlock(&peerl.lock);
 }
 
+static void destroy_user(struct iax2_user *user)
+{
+	ast_free_ha(user->ha);
+	free_context(user->contexts);
+	free(user);
+}
+
 static void prune_users(void)
 {
 	struct iax2_user *user, *usernext, *userlast = NULL;
@@ -6709,9 +6646,7 @@ static void prune_users(void)
 	for (user=userl.users;user;) {
 		usernext = user->next;
 		if (user->delme) {
-			ast_free_ha(user->ha);
-			free_context(user->contexts);
-			free(user);
+			destroy_user(user);
 			if (userlast)
 				userlast->next = usernext;
 			else
@@ -6723,32 +6658,37 @@ static void prune_users(void)
 	ast_mutex_unlock(&userl.lock);
 }
 
+static void destroy_peer(struct iax2_peer *peer)
+{
+	int x;
+	ast_free_ha(peer->ha);
+	for (x=0;x<IAX_MAX_CALLS;x++) {
+		ast_mutex_lock(&iaxsl[x]);
+		if (iaxs[x] && (iaxs[x]->peerpoke == peer)) {
+			iax2_destroy(x);
+		}
+		ast_mutex_unlock(&iaxsl[x]);
+	}
+	/* Delete it, it needs to disappear */
+	if (peer->expire > -1)
+		ast_sched_del(sched, peer->expire);
+	if (peer->pokeexpire > -1)
+		ast_sched_del(sched, peer->pokeexpire);
+	if (peer->callno > 0)
+		iax2_destroy(peer->callno);
+	register_peer_exten(peer, 0);
+	free(peer);
+}
+
 static void prune_peers(void){
 	/* Prune peers who still are supposed to be deleted */
 	struct iax2_peer *peer, *peerlast, *peernext;
-	int x;
 	ast_mutex_lock(&peerl.lock);
 	peerlast = NULL;
 	for (peer=peerl.peers;peer;) {
 		peernext = peer->next;
 		if (peer->delme) {
-			ast_free_ha(peer->ha);
-			for (x=0;x<IAX_MAX_CALLS;x++) {
-				ast_mutex_lock(&iaxsl[x]);
-				if (iaxs[x] && (iaxs[x]->peerpoke == peer)) {
-					iax2_destroy(x);
-				}
-				ast_mutex_unlock(&iaxsl[x]);
-			}
-			/* Delete it, it needs to disappear */
-			if (peer->expire > -1)
-				ast_sched_del(sched, peer->expire);
-			if (peer->pokeexpire > -1)
-				ast_sched_del(sched, peer->pokeexpire);
-			if (peer->callno > 0)
-				iax2_destroy(peer->callno);
-			register_peer_exten(peer, 0);
-			free(peer);
+			destroy_peer(peer);
 			if (peerlast)
 				peerlast->next = peernext;
 			else
@@ -6894,16 +6834,6 @@ static int set_config(char *config_file, struct sockaddr_in* sin){
 			} else {
 				amaflags = format;
 			}
-#ifdef MYSQL_FRIENDS
-		} else if (!strcasecmp(v->name, "dbuser")) {
-			strncpy(mydbuser, v->value, sizeof(mydbuser) - 1);
-		} else if (!strcasecmp(v->name, "dbpass")) {
-			strncpy(mydbpass, v->value, sizeof(mydbpass) - 1);
-		} else if (!strcasecmp(v->name, "dbhost")) {
-			strncpy(mydbhost, v->value, sizeof(mydbhost) - 1);
-		} else if (!strcasecmp(v->name, "dbname")) {
-			strncpy(mydbname, v->value, sizeof(mydbname) - 1);
-#endif
 		} else if (!strcasecmp(v->name, "language")) {
                         strncpy(language, v->value, sizeof(language) - 1);
 		} //else if (strcasecmp(v->name,"type"))
@@ -6943,21 +6873,6 @@ static int set_config(char *config_file, struct sockaddr_in* sin){
 	}
 	ast_destroy(cfg);
 	set_timing();
-#ifdef MYSQL_FRIENDS
-	/* Connect to db if appropriate */
-	if (!mysql && !ast_strlen_zero(mydbname)) {
-		mysql = mysql_init(NULL);
-		if (!mysql_real_connect(mysql, mydbhost[0] ? mydbhost : NULL, mydbuser, mydbpass, mydbname, 0, NULL, 0)) {
-			memset(mydbpass, '*', strlen(mydbpass));
-			ast_log(LOG_WARNING, "Database connection failed (db=%s, host=%s, user=%s, pass=%s)!\n",
-				mydbname, mydbhost, mydbuser, mydbpass);
-			free(mysql);
-			mysql = NULL;
-		} else
-			ast_verbose(VERBOSE_PREFIX_1 "Connected to database '%s' on '%s' as '%s'\n",
-				mydbname, mydbhost, mydbuser);
-	}
-#endif
 	return capability;
 }
 
diff --git a/config.c b/config.c
index bc9de3018a..5c972298a2 100755
--- a/config.c
+++ b/config.c
@@ -29,74 +29,45 @@
 
 
 static int ast_cust_config=0;
-struct ast_config *(*global_load_func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
-#ifdef PRESERVE_COMMENTS                                                                                                                                     
-,struct ast_comment_struct *
-#endif
-);
+struct ast_config *(*global_load_func)(const char *dbname, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
+
+static struct ast_config_map {
+	struct ast_config_map *next;
+	char *name;
+	char *driver;
+	char *database;
+	char *table;
+	char stuff[0];
+} *maps = NULL;
 
 AST_MUTEX_DEFINE_STATIC(ast_cust_config_lock);
 static struct ast_config_reg *ast_cust_config_list;
 static char *config_conf_file = "extconfig.conf";
 
-static char *strip(char *buf)
+void ast_destroy_realtime(struct ast_variable *v)
 {
-	char *start;
-	/* Strip off trailing whitespace, returns, etc */
-	while (!ast_strlen_zero(buf) && (buf[strlen(buf)-1]<33))
-		buf[strlen(buf)-1] = '\0';
-	start = buf;
-	/* Strip off leading whitespace, returns, etc */
-	while (*start && (*start < 33))
-		*start++ = '\0';
-	return start;
-}
-
-#ifdef PRESERVE_COMMENTS
-static void free_comments(struct ast_comment *com)
-{
-	struct ast_comment *l;
-	while (com) {
-		l = com;
-		com = com->next;
-		free(l);
+	struct ast_variable *vn;
+	while(v) {
+		vn = v;
+		v = v->next;
+		free(vn);
 	}
 }
-#endif
 
 void ast_destroy(struct ast_config *ast)
 {
 	struct ast_category *cat, *catn;
-	struct ast_variable *v, *vn;
 
 	if (!ast)
 		return;
 
 	cat = ast->root;
 	while(cat) {
-		v = cat->root;
-		while(v) {
-			vn = v;
-			free(v->name);
-			free(v->value);
-#ifdef PRESERVE_COMMENTS
-			free_comments(v->precomments);
-			free_comments(v->sameline);
-#endif			
-			v = v->next;
-			free(vn);
-		}
+		ast_destroy_realtime(cat->root);
 		catn = cat;
-#ifdef PRESERVE_COMMENTS
-		free_comments(cat->precomments);
-		free_comments(cat->sameline);
-#endif		
 		cat = cat->next;
 		free(catn);
 	}
-#ifdef PRESERVE_COMMENTS
-	free_comments(ast->trailingcomments);
-#endif	
 	free(ast);
 }
 
@@ -180,296 +151,88 @@ char *ast_variable_retrieve(struct ast_config *config, char *category, char *val
 	return NULL;
 }
 
-#ifdef PRESERVE_COMMENTS
-int ast_variable_delete(struct ast_config *cfg, char *category, char *variable, char *value)
+int ast_category_exist(struct ast_config *config, char *category_name)
 {
-	struct ast_variable *v, *pv, *bv, *bpv;
-	struct ast_category *cat;
-	cat = cfg->root;
-	while(cat) {
-		if (cat->name == category) {
-			break;
-		}
-		cat = cat->next;
-	}
-	if (!cat) {
-		cat = cfg->root;
-		while(cat) {
-			if (!strcasecmp(cat->name, category)) {
-				break;
-			}
-			cat = cat->next;
-		}
-	}
-	if (!cat)
-		return -1;
-	v = cat->root;
-	pv = NULL;
-	while (v) {
-		if ((variable == v->name) && (!value || !strcmp(v->value, value)))
-			break;
-		pv = v;
-		v=v->next;
-	}
-	if (!v) {
-		/* Get the last one that looks like it */
-		bv = NULL;
-		bpv = NULL;
-		v = cat->root;
-		pv = NULL;
-		while (v) {
-			if (!strcasecmp(variable, v->name) && (!value || !strcmp(v->value, value))) {
-				bv = v;
-				bpv = pv;
-			}
-			pv = v;
-			v=v->next;
-		}
-		v = bv;
-	}
+	struct ast_category *category = NULL;
 
-	if (v) {
-		/* Unlink from original position */
-		if (pv) 
-			pv->next = v->next;
-		else
-			cat->root = v->next;
-		v->next = NULL;
-		free(v->name);
-		if (v->value)
-			free(v->value);
-		free_comments(v->sameline);
-		free_comments(v->precomments);
-		return 0;
-	}
-	return -1;
+	category = config->root;
+
+	while(category) {
+		if (!strcasecmp(category->name,category_name)) 
+			return 1;
+		category = category->next;
+	} 
+
+	return 0;
 }
 
-int ast_category_delete(struct ast_config *cfg, char *category)
+
+static struct ast_config_reg *get_ast_cust_config_keyword(const char *name, char *database, int dbsiz, char *table, int tabsiz) 
 {
-	struct ast_variable *v, *pv;
-	struct ast_category *cat, *cprev;
-	cat = cfg->root;
-	cprev = NULL;
-	while(cat) {
-		if (cat->name == category) {
+	struct ast_config_reg *reg,*ret=NULL;
+	struct ast_config_map *map;
+
+	ast_mutex_lock(&ast_cust_config_lock);
+	map = maps;
+	while(map) {
+		if (!strcasecmp(name, map->name)) {
+			strncpy(database, map->database, dbsiz - 1);
+			if (map->table)
+				strncpy(table, map->table, tabsiz - 1);
+			else
+				strncpy(table, name, tabsiz - 1);
 			break;
 		}
-		cprev = cat;
-		cat = cat->next;
+		map = map->next;
 	}
-	if (!cat) {
-		cat = cfg->root;
-		cprev = NULL;
-		while(cat) {
-			if (!strcasecmp(cat->name, category)) {
-				break;
-			}
-			cprev = cat;
-			cat = cat->next;
+	if (map) {
+		for (reg=ast_cust_config_list;reg && !ret;reg=reg->next) {
+			if (!strcmp(reg->name,map->driver))
+				ret=reg;
 		}
 	}
-	if (!cat)
-		return -1;
-	/* Unlink it */
-	if (cprev)
-		cprev->next = cat->next;
-	else
-		cfg->root = cat->next;
-	v = cat->root;
-	while (v) {
-		pv = v;
-		v=v->next;
-		if (pv->value)
-			free(pv->value);
-		if (pv->name)
-			free(pv->name);
-		free_comments(pv->sameline);
-		free_comments(pv->precomments);
-		free(pv);
-	}
-	free_comments(cat->sameline);
-	free_comments(cat->precomments);
-	free(cat);
-	return 0;
+	ast_mutex_unlock(&ast_cust_config_lock);
+	return ret;
 }
 
-struct ast_variable *ast_variable_append_modify(struct ast_config *config, char *category, char *variable, char *value, int newcat, int newvar, int move)
+void ast_config_destroy_all(void) 
 {
-	struct ast_variable *v, *pv=NULL, *bv, *bpv;
-	struct ast_category *cat, *pcat;
-	cat = config->root;
-	if (!newcat) {
-		while(cat) {
-			if (cat->name == category) {
-				break;
-			}
-			cat = cat->next;
-		}
-		if (!cat) {
-			cat = config->root;
-			while(cat) {
-				if (!strcasecmp(cat->name, category)) {
-					break;
-				}
-				cat = cat->next;
-			}
-		}
-	}
-	if (!cat) {
-		cat = malloc(sizeof(struct ast_category));
-		if (!cat)
-			return NULL;
-		memset(cat, 0, sizeof(struct ast_category));
-		strncpy(cat->name, category, sizeof(cat->name) - 1);
-		if (config->root) {
-			/* Put us at the end */
-			pcat = config->root;
-			while(pcat->next)
-				pcat = pcat->next;
-			pcat->next = cat;
-		} else {
-			/* We're the first one */
-			config->root = cat;
-		}
-			
-	}
-	if (!newvar) {
-		v = cat->root;
-		pv = NULL;
-		while (v) {
-			if (variable == v->name)
-				break;
-			pv = v;
-			v=v->next;
-		}
-		if (!v) {
-			/* Get the last one that looks like it */
-			bv = NULL;
-			bpv = NULL;
-			v = cat->root;
-			pv = NULL;
-			while (v) {
-				if (!strcasecmp(variable, v->name)) {
-					bv = v;
-					bpv = pv;
-				}
-				pv = v;
-				v=v->next;
-			}
-			v = bv;
-		}
-	} else v = NULL;
-	if (v && move) {
-		/* Unlink from original position */
-		if (pv) 
-			pv->next = v->next;
-		else
-			cat->root = v->next;
-		v->next = NULL;
-	}
-	if (!v) {
-		v = malloc(sizeof(struct ast_variable));
-		if (!v)
-			return NULL;
-		memset(v, 0, sizeof(struct ast_variable));
-		v->name = strdup(variable);
-		move = 1;
-	}
-	if (v->value)
-		free(v->value);
-	if (value)
-		v->value = strdup(value);
-	else
-		v->value = strdup("");
-	if (move) {
-		if (cat->root) {
-			pv = cat->root;
-			while (pv->next) 
-				pv = pv->next;
-			pv->next = v;
-		} else {
-			cat->root = v;
-		}
+	struct ast_config_reg *key;
+	ast_mutex_lock(&ast_cust_config_lock);
+	for (key=ast_cust_config_list;key;key=key->next) {
+		ast_config_deregister(key);
 	}
-	return v;
+	ast_cust_config_list = NULL;
+	ast_mutex_unlock(&ast_cust_config_lock);
 }
-#endif		
 
-int ast_category_exist(struct ast_config *config, char *category_name)
+static struct ast_config_reg *get_config_registrations(void) 
 {
-	struct ast_category *category = NULL;
-
-	category = config->root;
-
-	while(category) {
-		if (!strcasecmp(category->name,category_name)) 
-			return 1;
-		category = category->next;
-	} 
-
-	return 0;
+	return ast_cust_config_list;
 }
 
-#ifdef PRESERVE_COMMENTS
-static struct ast_comment *build_comment(char *cmt)
-{
-	struct ast_comment *c;
-	int len = strlen(cmt) + 1;
-	c = malloc(sizeof(struct ast_comment) + len);
-	if (c) {
-		/* Memset the header */
-		memset(c, 0, sizeof(struct ast_comment));
-		/* Copy the rest */
-		strcpy(c->cmt, cmt);
-	}
-	return c;
-}
-#endif
 
-static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel
-#ifdef PRESERVE_COMMENTS
-, struct ast_comment_struct *acs
-#endif
-);
+static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel);
 
-static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, char *configfile, int includelevel 
-#ifdef PRESERVE_COMMENTS
-,struct ast_comment_struct *acs
-#endif
-)
+static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, const char *configfile, int includelevel )
 {
 	char *c;
 	char *cur;
 	char *arg=NULL;
-	struct ast_config_reg *reg=NULL;
-	struct ast_config *(*load_func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
-#ifdef PRESERVE_COMMENTS
-,struct ast_comment_struct *
-#endif
-	);
 	struct ast_variable *v;
-#ifdef PRESERVE_COMMENTS
-	struct ast_comment *com = NULL;
-#endif	
 	int object;
 	/* Strip off lines using ; as comment */
 	c = strchr(buf, ';');
 	while (c) {
 		if ((c == buf) || (*(c-1) != '\\')) {
 			*c = '\0';
-#ifdef PRESERVE_COMMENTS
-			c++;
-			if (*c != '!')
-				com = build_comment(c);
-#endif			
 		} else {
 			*(c-1) = ';';
 			memmove(c, c + 1, strlen(c + 1));
 		}
 		c = strchr(c + 1, ';');
 	}
-	cur = strip(buf);
+	cur = ast_strip(buf);
 	if (!ast_strlen_zero(cur)) {
 		/* Actually parse the entry */
 		if (cur[0] == '[') {
@@ -487,20 +250,12 @@ static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, stru
 				memset(*_tmpc, 0, sizeof(struct ast_category));
 				strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
 				(*_tmpc)->root =  NULL;
-#ifdef PRESERVE_COMMENTS
-				(*_tmpc)->precomments = acs->root;
-				(*_tmpc)->sameline = com;
-#endif				
 				if (!tmp->prev)
 					tmp->root = *_tmpc;
 				else
 					tmp->prev->next = *_tmpc;
 
 				tmp->prev = *_tmpc;
-#ifdef PRESERVE_COMMENTS
-				acs->root = NULL;
-				acs->prev = NULL;
-#endif				
 				*_last =  NULL;
 			} else {
 				ast_log(LOG_WARNING, 
@@ -542,28 +297,9 @@ static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, stru
 					
 					if (includelevel < MAX_INCLUDE_LEVEL) {
 						if(arg && cur) {
-							load_func = NULL;
-							if(ast_cust_config_list)
-								reg = get_ast_cust_config(cur);
-							if(reg && reg->func)
-								load_func = reg->func;
-							if(load_func) { 
-								ast_log(LOG_NOTICE,"External Include '%s' via '%s' config engine\n",arg,cur);
-								load_func(arg,tmp, _tmpc, _last, includelevel
-#ifdef PRESERVE_COMMENTS
-										  ,&acs
-#endif
-										  );
-							}
-							else 
-								ast_log(LOG_WARNING,"Cant Find Registered Config Engine [%s] for [%s]\n",cur,arg);
-						}
-						else {
-							__ast_load(cur, tmp, _tmpc, _last, includelevel + 1
-#ifdef PRESERVE_COMMENTS
-									   ,acs
-#endif
-									   );
+							ast_log(LOG_WARNING, "Including files with explicit config engine no longer permitted.  Please use extconfig.conf to specify all mappings\n");
+						} else {
+							__ast_load(cur, tmp, _tmpc, _last, includelevel + 1);
 						}
 					} else
 						ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
@@ -591,21 +327,12 @@ static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, stru
 					c++;
 				} else
 					object = 0;
-				v = malloc(sizeof(struct ast_variable));
+				v = ast_new_variable(ast_strip(cur), ast_strip(c));
 				if (v) {
-					memset(v, 0, sizeof(struct ast_variable));
 					v->next = NULL;
-					v->name = strdup(strip(cur));
-					v->value = strdup(strip(c));
 					v->lineno = lineno;
 					v->object = object;
 					/* Put and reset comments */
-#ifdef PRESERVE_COMMENTS
-					v->precomments = acs->root;
-					v->sameline = com;
-					acs->prev = NULL;
-					acs->root = NULL;
-#endif					
 					v->blanklines = 0;
 					if (*_last)
 						(*_last)->next = v;
@@ -622,34 +349,10 @@ static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, stru
 			}
 														
 		}
-	} else {
-		/* store any comments if there are any */
-#ifdef PRESERVE_COMMENTS
-		if (com) {
-			if (acs->prev)
-				acs->prev->next = com;
-			else
-				acs->root = com;
-			acs->prev = com;
-		} else {
-			if (*_last) 
-				(*_last)->blanklines++;
-		}
-#endif
 	}
 	return 0;
 }
 
-#ifdef PRESERVE_COMMENTS
-static void dump_comments(FILE *f, struct ast_comment *comment)
-{
-	while (comment) {
-		fprintf(f, ";%s", comment->cmt);
-		comment = comment->next;
-	}
-}
-#endif
-
 int ast_save(char *configfile, struct ast_config *cfg, char *generator)
 {
 	FILE *f;
@@ -677,22 +380,10 @@ int ast_save(char *configfile, struct ast_config *cfg, char *generator)
 		fprintf(f, ";!\n");
 		cat = cfg->root;
 		while(cat) {
-#ifdef PRESERVE_COMMENTS
-			/* Dump any precomments */
-			dump_comments(f, cat->precomments);
-#endif
 			/* Dump section with any appropriate comment */
-#ifdef PRESERVE_COMMENTS
-			if (cat->sameline) 
-				fprintf(f, "[%s]  ; %s\n", cat->name, cat->sameline->cmt);
-			else
-#endif
-				fprintf(f, "[%s]\n", cat->name);
+			fprintf(f, "[%s]\n", cat->name);
 			var = cat->root;
 			while(var) {
-#ifdef PRESERVE_COMMENTS
-				dump_comments(f, var->precomments);
-#endif				
 				if (var->sameline) 
 					fprintf(f, "%s %s %s  ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
 				else	
@@ -713,9 +404,6 @@ int ast_save(char *configfile, struct ast_config *cfg, char *generator)
 #endif
 			cat = cat->next;
 		}
-#ifdef PRESERVE_COMMENTS
-		dump_comments(f, cfg->trailingcomments);
-#endif		
 	} else {
 		if (option_debug)
 			printf("Unable to open for writing: %s\n", fn);
@@ -727,46 +415,63 @@ int ast_save(char *configfile, struct ast_config *cfg, char *generator)
 	return 0;
 }
 
-static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel
-#ifdef PRESERVE_COMMENTS
-, struct ast_comment_struct *acs
-#endif
-)
+
+struct ast_variable *ast_load_realtime(const char *family, const char *keyfield, const char *lookup)
+{
+	struct ast_config_reg *reg;
+	char db[256]="";
+	char table[256]="";
+	reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
+	if (reg && reg->realtime_func) 
+		return reg->realtime_func(db, table, keyfield, lookup);
+	return NULL;
+}
+
+int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
+{
+	struct ast_config_reg *reg;
+	int res = -1;
+	char db[256]="";
+	char table[256]="";
+	va_list ap;
+	va_start(ap, lookup);
+	reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
+	if (reg && reg->update_func) 
+		res = reg->update_func(db, table, keyfield, lookup, ap);
+	va_end(ap);
+	return res;
+}
+
+static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel)
 {
 	char fn[256];
 	char buf[8192];
+	char db[256];
+	char table[256];
 	FILE *f;
 	int lineno=0;
 	int master=0;
 	struct ast_config_reg *reg=NULL;
-	struct ast_config *(*load_func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
-#ifdef PRESERVE_COMMENTS
-,struct ast_comment_struct *
-#endif
-);
+	struct ast_config *(*load_func)(const char *database, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
 
 	load_func=NULL;
 	if (strcmp(configfile,config_conf_file) && strcmp(configfile,"asterisk.conf") && ast_cust_config_list) {
 		if (global_load_func) {
 			load_func = global_load_func;
 		} else {
-			reg = get_ast_cust_config_keyword(configfile);
-			if (reg && reg->func) {
-				load_func = reg->func;
+			reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
+			if (reg && reg->static_func) {
+				load_func = reg->static_func;
 			} else {
-				reg = get_ast_cust_config_keyword("global");
-				if (reg && reg->func)
-					global_load_func = load_func = reg->func;
+				reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
+				if (reg && reg->static_func)
+					global_load_func = load_func = reg->static_func;
 			}
 		}
 
 		if (load_func) {
 			ast_log(LOG_NOTICE,"Loading Config %s via %s engine\n",configfile,reg && reg->name ? reg->name : "global");
-			tmp = load_func(configfile,tmp, _tmpc, _last, includelevel
-#ifdef PRESERVE_COMMENTS
-,&acs
-#endif
-);
+			tmp = load_func(db, table, configfile,tmp, _tmpc, _last, includelevel);
 	    
 			if (tmp)
 				return tmp;
@@ -802,11 +507,7 @@ static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, s
 		while(!feof(f)) {
 			lineno++;
 			if (fgets(buf, sizeof(buf), f)) {
-				if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel
-#ifdef PRESERVE_COMMENTS
-				, acs
-#endif
-				)) {
+				if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel)) {
 					fclose(f);
 					return NULL;
 				}
@@ -819,65 +520,13 @@ static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, s
 		else if (option_verbose > 1)
 			ast_verbose( "Not found (%s)\n", strerror(errno));
 	}
-#ifdef PRESERVE_COMMENTS
-	if (master) {
-		/* Keep trailing comments */
-		tmp->trailingcomments = acs->root;
-		acs->root = NULL;
-		acs->prev = NULL;
-	}
-#endif
 	return tmp;
 }
 
-struct ast_config_reg *get_ast_cust_config_keyword(char *name) 
-{
-	struct ast_config_reg *reg,*ret=NULL;
-	int x=0;
-	ast_mutex_lock(&ast_cust_config_lock);
-	for (reg=ast_cust_config_list;reg && !ret;reg=reg->next) {
-		for (x=0;x<reg->keycount && !ret ;x++) {
-			if (!strcmp(reg->keywords[x],name))
-				ret=reg;
-		}
-	}
-	ast_mutex_unlock(&ast_cust_config_lock);
-	return ret;
-}
-
-struct ast_config_reg *get_ast_cust_config(char *name) 
-{
-	struct ast_config_reg *ptr=NULL;
-	ast_mutex_lock(&ast_cust_config_lock);
-	for (ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
-		if (!strcmp(name,ptr->name))
-			break;
-	}
-	ast_mutex_unlock(&ast_cust_config_lock);
-	return ptr;
-}
-
-void ast_config_destroy_all(void) 
-{
-	struct ast_config_reg *key;
-	ast_mutex_lock(&ast_cust_config_lock);
-	for (key=ast_cust_config_list;key;key=key->next) {
-		ast_config_deregister(key);
-	}
-	ast_cust_config_list = NULL;
-	ast_mutex_unlock(&ast_cust_config_lock);
-}
-
-struct ast_config_reg *get_config_registrations(void) 
-{
-	return ast_cust_config_list;
-}
-
 int ast_config_register(struct ast_config_reg *new) 
 {
 	struct ast_config_reg *ptr;
 	ast_mutex_lock(&ast_cust_config_lock);
-	new->keycount = 0;
 	if (!ast_cust_config_list) {
 		ast_cust_config_list = new;
 	} else {
@@ -920,17 +569,7 @@ struct ast_config *ast_load(char *configfile)
 	struct ast_category *tmpc=NULL;
 	struct ast_variable *last = NULL;
 
-
-#ifdef PRESERVE_COMMENTS
-	struct ast_comment_struct acs = { NULL, NULL };
-#endif	
-
-
-	return __ast_load(configfile, NULL, &tmpc, &last, 0 
-#ifdef PRESERVE_COMMENTS
-	,&acs
-#endif
-	);
+	return __ast_load(configfile, NULL, &tmpc, &last, 0);
 }
 
 char *ast_category_browse(struct ast_config *config, char *prev)
@@ -991,27 +630,16 @@ struct ast_category *ast_new_category(char *name)
 struct ast_variable *ast_new_variable(char *name, char *value) 
 {
 	struct ast_variable *variable;
-	variable = malloc(sizeof(struct ast_variable));
+	int length = strlen(name) + strlen(value) + 2 + sizeof(struct ast_variable);
+	variable = malloc(length);
 	if (variable) {
-		memset(variable,0,sizeof(struct ast_variable));
+		memset(variable, 0, length);
+		variable->name = variable->stuff;
+		variable->value = variable->stuff + strlen(name) + 1;		
 		variable->object=0;
-		variable->name = malloc(strlen(name)+1);
-		if (variable->name) {
-			strcpy(variable->name,name);
-			variable->value = malloc(strlen(value)+1);
-			if (variable->value) {
-				strcpy(variable->value,value);
-			} else {
-				free(variable->name);
-				variable->name = NULL;
-			}
-		}
-	}
-	if (!variable->value) {
-		free(variable);
-		variable = NULL;
+		strcpy(variable->name,name);
+		strcpy(variable->value,value);
 	}
-		
 	return variable;
 }
 
@@ -1030,29 +658,33 @@ int ast_cust_config_deregister(struct ast_config_reg *new)
 
 static void clear_cust_keywords(void) 
 {
-	struct ast_config_reg *key;
-	int x;
+	struct ast_config_map *map, *prev;
 	ast_mutex_lock(&ast_cust_config_lock);
-	for (key=get_config_registrations();key;key=key->next) {
-		for (x=0;x<key->keycount;x++) {
-			key->keywords[x][0] = '\0';
-		}
-		key->keycount=0;
+	map = maps;
+	while(map) {
+		prev = map;
+		map = map->next;
+		free(prev);
 	}
+	maps = NULL;
 	ast_mutex_unlock(&ast_cust_config_lock);
 }
 
 static int config_command(int fd, int argc, char **argv) 
 {
 	struct ast_config_reg *key;
-	int x;
+	struct ast_config_map *map;
 	
 	ast_cli(fd,"\n\n");
 	ast_mutex_lock(&ast_cust_config_lock);
 	for (key=get_config_registrations();key;key=key->next) {
 		ast_cli(fd,"\nConfig Engine: %s\n",key->name);
-		for (x=0;x<key->keycount;x++)
-			ast_cli(fd,"===>%s\n",key->keywords[x]);
+		map = maps;
+		while(map) {
+			if (!strcasecmp(map->driver, key->name))
+				ast_cli(fd,"===> %s (db=%s, table=%s)\n",map->name, map->database, map->table ? map->table : map->name);
+			map = map->next;
+		}
 	}
 	ast_mutex_unlock(&ast_cust_config_lock);
 	ast_cli(fd,"\n\n");
@@ -1074,29 +706,46 @@ int read_ast_cust_config(void)
 	char *cfg = config_conf_file;
 	struct ast_config *config;
 	struct ast_variable *v;
-	struct ast_config_reg *ptr;
-	struct ast_config_reg *test = NULL;
+	struct ast_config_map *map;
+	int length;
+	char *driver, *table, *database, *stringp;
 
 	clear_cust_keywords();
 	config = ast_load(cfg);
 	if (config) {
 		for (v = ast_variable_browse(config,"settings");v;v=v->next) {
+			stringp = v->value;
+			driver = strsep(&stringp, ",");
+			database = strsep(&stringp, ",");
+			table = strsep(&stringp, ",");
 			
-			ptr = get_ast_cust_config(v->value);
-			if (ptr) {
-				if (ptr->keycount >= CONFIG_KEYWORD_ARRAYLEN) {
-					ast_log(LOG_WARNING,"Max Number of Bindings exceeded for %s->%s %d/%d\n",v->name,v->value,ptr->keycount,CONFIG_KEYWORD_ARRAYLEN);
-				} else {
-					if (strcmp(v->name,config_conf_file) && strcmp(v->name,"asterisk.conf")) {
-						if (!(test = get_ast_cust_config_keyword(v->name))) {
-							ast_log(LOG_NOTICE,"Binding: %s to %s\n",v->name,v->value);
-							strncpy(ptr->keywords[ptr->keycount],v->name,sizeof(ptr->keywords[ptr->keycount]) - 1);
-							ptr->keywords[ptr->keycount][sizeof(ptr->keywords[ptr->keycount])-1] = '\0';
-							ptr->keycount++;
-						}
-					} else {
-						ast_log(LOG_WARNING,"Cannot bind %s, Permission Denied\n",v->name);
-					}
+			if (!strcmp(v->name,config_conf_file) || !strcmp(v->name,"asterisk.conf")) {
+				ast_log(LOG_WARNING, "Cannot bind asterisk.conf or extconfig.conf!\n");
+			} else if (driver && database) {
+				length = sizeof(struct ast_config_map);
+				length += strlen(v->name) + 1;
+				length += strlen(driver) + 1;
+				length += strlen(database) + 1;
+				if (table)
+					length += strlen(table) + 1;
+				map = malloc(length);
+				if (map) {
+					memset(map, 0, length);
+					map->name = map->stuff;
+					strcpy(map->name, v->name);
+					map->driver = map->name + strlen(map->name) + 1;
+					strcpy(map->driver, driver);
+					map->database = map->driver + strlen(map->driver) + 1;
+					strcpy(map->database, database);
+					if (table) {
+						map->table = map->database + strlen(map->database) + 1;
+						strcpy(map->table, table);
+					} else
+						map->table = NULL;
+					map->next = maps;
+					if (option_verbose > 1)
+						ast_verbose(VERBOSE_PREFIX_2 "Binding %s to %s/%s/%s\n",map->name,map->driver, map->database, map->table ? map->table : map->name);
+					maps = map;
 				}
 			}
 		}
diff --git a/configs/extconfig.conf.sample b/configs/extconfig.conf.sample
index 50776fbdb1..957ff3702d 100755
--- a/configs/extconfig.conf.sample
+++ b/configs/extconfig.conf.sample
@@ -1,6 +1,28 @@
+;
+; Static and realtime external configuration
+; engine configuration
+;
 [settings]
+;
+; Static configuration files: 
+;
+; file.conf => driver,database[,table]
+;
+; maps a particular configuration file to the given
+; database driver, database and table (or uses the
+; name of the file as the table if not specified)
+;
+;uncomment to load queues.conf via the odbc engine.
+;
+;queues.conf => odbc,asterisk,ast_config
 
-;uncomment to load queues.conf via the db engine.
-;queues.conf => odbc
-
+;
+; Realtime configuration engine
+;
+; maps a particular family of realtime
+; configuration to a given database driver,
+; database and table (or uses the name of
+; the family if the table is not specified
+;
+;iaxfriends => odbc,asterisk
 
diff --git a/configs/res_config_odbc.conf.sample b/configs/res_config_odbc.conf.sample
deleted file mode 100755
index f11cf94814..0000000000
--- a/configs/res_config_odbc.conf.sample
+++ /dev/null
@@ -1,3 +0,0 @@
-[settings]
-table => ast_config
-connection => mysql1
diff --git a/doc/README.extconfig b/doc/README.extconfig
new file mode 100755
index 0000000000..2602632086
--- /dev/null
+++ b/doc/README.extconfig
@@ -0,0 +1,38 @@
+Asterisk external configuration
+===============================
+
+The Asterisk external configuration engine is the result of work by 
+Anthony Minessale II and Mark Spencer.  It is designed to provide a
+flexible, seamless integration between Asterisk's internal
+configuration structure and external SQL other other databases
+(maybe even LDAP one day).
+
+External configuration is configured in /etc/asterisk/extconfig.conf
+allowing you to map any configuration file (static mappings) to
+be pulled from the database, or to map special runtime entries which
+permit the dynamic creation of objects, entities, peers, etc. without
+the necessity of a reload.
+
+Generally speaking, the columns in your tables should line up with the
+fields you would specify in the given entity declaration.  If an entry
+would appear more than once, in the column it should be separated by a
+semicolon. For example, an entity that looks like:
+
+[foo]
+host=dynamic
+secret=bar
+context=default
+context=local
+
+could be stored in a table like this:
+
++------+--------+-------+--------------+----------+-----+-----------+
+| name | host   | secret| context      | ipaddr   | port| regseconds|
++------+--------+-------+--------------+----------+-----+-----------+
+| foo  | dynamic|  bar  | default;local| 127.0.0.1| 4569| 1096954152|
++------+--------+-------+--------------+----------+-----+-----------+
+
+Note that for use with IAX or SIP, the table will also need the "name", 
+"ipaddr", "port", "regseconds" columns.  If you wanted to be able to 
+configure the callerid, you could just add a callerid column to the 
+table, for example.
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index e98df700f4..89e66437bb 100755
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -34,6 +34,7 @@ struct ast_variable {
 	struct ast_comment *precomments;
 	struct ast_comment *sameline;
 	struct ast_variable *next;
+	char stuff[0];
 };
 
 //! Load a config file
@@ -103,12 +104,37 @@ int ast_false(char *val);
  * Browse config structure and check for category duplicity Return non-zero if found */
 int ast_category_exist(struct ast_config *config, char *category_name);
 
-/* These are only in the config engine at this point */
-struct ast_variable *ast_variable_append_modify(struct ast_config *cfg, char *category, char *variable, char *newvalue, int newcat, int newvar, int move);
+//! Retrieve realtime configuration
+/*!
+ * \param family which family/config to lookup
+ * \param keyfield which field to use as the key
+ * \param lookup which value to look for in the key field to match the entry.
+ * This will use builtin configuration backends to look up a particular 
+ * entity in realtime and return a variable list of its parameters.  Note
+ * that unlike the variables in ast_config, the resulting list of variables
+ * MUST be fred with ast_free_runtime() as there is no container.
+ */
+struct ast_variable *ast_load_realtime(const char *family, const char *keyfield, const char *lookup);
+
+//! Update realtime configuration
+/*!
+ * \param family which family/config to be updated
+ * \param keyfield which field to use as the key
+ * \param lookup which value to look for in the key field to match the entry.
+ * \param variable which variable should be updated in the config, NULL to end list
+ * \param value the value to be assigned to that variable in the given entity.
+ * This function is used to update a parameter in realtime configuration space.
+ *
+ */
+int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...);
 
-int ast_category_delete(struct ast_config *cfg, char *category);
-int ast_variable_delete(struct ast_config *cfg, char *category, char *variable, char *value);
-int ast_save(char *filename, struct ast_config *cfg, char *generator);
+//! Free realtime configuration
+/*!
+ * \param var the linked list of variables to free
+ * This command free's a list of variables and should ONLY be used
+ * in conjunction with ast_load_realtime and not with the regular ast_load.
+ */
+void ast_destroy_realtime(struct ast_variable *var);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }
diff --git a/include/asterisk/config_pvt.h b/include/asterisk/config_pvt.h
index f87189d316..e33d92de9f 100755
--- a/include/asterisk/config_pvt.h
+++ b/include/asterisk/config_pvt.h
@@ -4,6 +4,7 @@
 extern "C" {
 #endif
 
+#include <stdarg.h>
 #define CONFIG_KEYWORD_STRLEN 128
 #define CONFIG_KEYWORD_ARRAYLEN 512
 #include <asterisk/config.h>
@@ -14,10 +15,6 @@ struct ast_category {
 	char name[80];
 	struct ast_variable *root;
 	struct ast_category *next;
-#ifdef PRESERVE_COMMENTS
-	struct ast_comment *precomments;
-	struct ast_comment *sameline;
-#endif	
 };
 
 struct ast_config {
@@ -25,43 +22,25 @@ struct ast_config {
 	   for now */
 	struct ast_category *root;
 	struct ast_category *prev;
-#ifdef PRESERVE_COMMENTS
-	struct ast_comment *trailingcomments;
-#endif	
 };
 
-#ifdef PRESERVE_COMMENTS
-struct ast_comment_struct
-{
-	struct ast_comment *root;
-	struct ast_comment *prev;
-};
-#endif
 
 struct ast_category;
 
 struct ast_config_reg {
 	char name[CONFIG_KEYWORD_STRLEN];
-	struct ast_config *(*func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
-#ifdef PRESERVE_COMMENTS
-,struct ast_comment_struct *
-#endif
-);
-	char keywords[CONFIG_KEYWORD_STRLEN][CONFIG_KEYWORD_ARRAYLEN];
-	int keycount;
+	struct ast_config *(*static_func)(const char *database, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
+	struct ast_variable *(*realtime_func)(const char *database, const char *table,  const char *keyfield, const char *entity);
+	int (*update_func)(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
 	struct ast_config_reg *next;
 };
 
 
-
 int ast_config_register(struct ast_config_reg *new);
 int ast_config_deregister(struct ast_config_reg *del);
 void ast_cust_config_on(void);
 void ast_cust_config_off(void);
 int ast_cust_config_active(void);
-struct ast_config_reg *get_config_registrations(void);
-struct ast_config_reg *get_ast_cust_config(char *name);
-struct ast_config_reg *get_ast_cust_config_keyword(char *name);
 void ast_config_destroy_all(void);
 
 
diff --git a/include/asterisk/res_odbc.h b/include/asterisk/res_odbc.h
index 1a68acb983..9b0f71abea 100755
--- a/include/asterisk/res_odbc.h
+++ b/include/asterisk/res_odbc.h
@@ -45,7 +45,7 @@ odbc_status odbc_obj_connect(odbc_obj *obj);
 odbc_status odbc_obj_disconnect(odbc_obj *obj);
 void destroy_obdc_obj(odbc_obj **obj);
 int register_odbc_obj(char *name,odbc_obj *obj);
-odbc_obj *fetch_odbc_obj(char *name);
+odbc_obj *fetch_odbc_obj(const char *name);
 int odbc_dump_fd(int fd,odbc_obj *obj);
 
 #endif
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index bfbdd951c8..4543cd498b 100755
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -26,6 +26,8 @@ struct ast_hostent {
 	char buf[1024];
 };
 
+
+extern char *ast_strip(char *buf);
 extern struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp);
 extern int ast_base64encode(char *dst, unsigned char *src, int srclen, int max);
 extern int ast_base64decode(unsigned char *dst, char *src, int max);
diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c
index 3f8490530c..2c16e5135a 100755
--- a/res/res_config_odbc.c
+++ b/res/res_config_odbc.c
@@ -1,9 +1,9 @@
 /*
  * Asterisk -- A telephony toolkit for Linux.
  *
- * Copyright (C) 1999, Mark Spencer
+ * Copyright (C) 1999-2004, Digium, Inc.
  *
- * Mark Spencer <markster@linux-support.net>
+ * Mark Spencer <markster@digium.com>
  *
  * res_config_odbc.c <odbc+odbc plugin for portable configuration engine >
  * Copyright (C) 2004 Anthony Minessale II <anthmct@yahoo.com>
@@ -17,10 +17,12 @@
 #include <asterisk/config_pvt.h>
 #include <asterisk/module.h>
 #include <asterisk/lock.h>
+#include <asterisk/options.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <asterisk/res_odbc.h>
+#include <asterisk/utils.h>
 
 static char *tdesc = "ODBC Configuration";
 static struct ast_config_reg reg1;
@@ -29,18 +31,200 @@ STANDARD_LOCAL_USER;
 
 LOCAL_USER_DECL;
 
-static struct ast_config *config_odbc (char *file, struct ast_config *new_config_s, struct ast_category **new_cat_p, struct ast_variable **new_v_p, int recur
-#ifdef PRESERVE_COMMENTS
-	, struct ast_comment_struct *acs
-#endif
-)
+static struct ast_variable *realtime_odbc(const char *database, const char *table, const char *keyfield, const char *lookup)
 {
-	struct ast_config *config, *new;
-	struct ast_variable *v, *cur_v, *new_v;
+	odbc_obj *obj;
+	SQLHSTMT stmt;
+	char sql[256];
+	char coltitle[256];
+	char rowdata[2048];
+	char *stringp;
+	char *chunk;
+	SQLSMALLINT collen;
+	int res;
+	int x;
+	struct ast_variable *var=NULL, *prev=NULL;
+	SQLLEN rowcount=0;
+	SQLULEN colsize;
+	SQLSMALLINT colcount=0;
+	SQLSMALLINT datatype;
+	SQLSMALLINT decimaldigits;
+	SQLSMALLINT nullable;
+	
+	if (!table)
+		return NULL;
+
+	obj = fetch_odbc_obj(database);
+	if (!obj)
+		return NULL;
+
+	res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+		return NULL;
+	}
+
+	snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s=?", table, keyfield);
+	
+	res = SQLPrepare(stmt, sql, SQL_NTS);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+		return NULL;
+	}
+	SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(lookup), 0, (void *)lookup, 0, NULL);
+
+	res = SQLExecute(stmt);
+
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+		return NULL;
+	}
+
+	res = SQLRowCount(stmt, &rowcount);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+		return NULL;
+	}
+
+	res = SQLNumResultCols(stmt, &colcount);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+		return NULL;
+	}
+
+	if (rowcount) {
+		res = SQLFetch(stmt);
+		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+			ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			return NULL;
+		}
+		for (x=0;x<colcount;x++) {
+			collen = sizeof(coltitle);
+			res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
+						&datatype, &colsize, &decimaldigits, &nullable);
+			if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+				ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+				if (var)
+					ast_destroy_realtime(var);
+				return NULL;
+			}
+			res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+			if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+				ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+				if (var)
+					ast_destroy_realtime(var);
+				return NULL;
+			}
+			stringp = rowdata;
+			while(stringp) {
+				chunk = strsep(&stringp, ";");
+				if (chunk && !ast_strlen_zero(ast_strip(chunk))) {
+					if (prev) {
+						prev->next = ast_new_variable(coltitle, chunk);
+						if (prev->next)
+							prev = prev->next;
+					} else 
+						prev = var = ast_new_variable(coltitle, chunk);
+					
+				}
+			}
+		}
+	}
+
+
+	SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	return var;
+}
+
+static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
+{
+	odbc_obj *obj;
+	SQLHSTMT stmt;
+	char sql[256];
+	SQLLEN rowcount=0;
+	const char *newparam, *newval;
+	int res;
+	int x;
+	va_list aq;
+	
+	va_copy(aq, ap);
+	
+	if (!table)
+		return -1;
+
+	obj = fetch_odbc_obj (database);
+	if (!obj)
+		return -1;
+
+	res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+		return -1;
+	}
+
+	newparam = va_arg(aq, const char *);
+	if (!newparam)  {
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+		return -1;
+	}
+	newval = va_arg(aq, const char *);
+	snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
+	while((newparam = va_arg(aq, const char *))) {
+		snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
+		newval = va_arg(aq, const char *);
+	}
+	va_end(aq);
+	snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
+	
+	res = SQLPrepare(stmt, sql, SQL_NTS);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+		return -1;
+	}
+	
+	/* Now bind the parameters */
+	x = 1;
+
+	while((newparam = va_arg(ap, const char *))) {
+		newval = va_arg(ap, const char *);
+		SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
+	}
+		
+	SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(lookup), 0, (void *)lookup, 0, NULL);
+
+	res = SQLExecute(stmt);
+
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+		return -1;
+	}
+
+	res = SQLRowCount(stmt, &rowcount);
+	SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+		return -1;
+	}
+
+	if (rowcount) 
+		return 0;
+	return -1;
+}
+
+static struct ast_config *config_odbc (const char *database, const char *table, const char *file, struct ast_config *new_config_s, struct ast_category **new_cat_p, struct ast_variable **new_v_p, int recur)
+{
+	struct ast_config *new;
+	struct ast_variable *cur_v, *new_v;
 	struct ast_category *cur_cat, *new_cat;
-	char table[128] = "";
-	char connection[128] = "";
-	int configured = 0, res = 0;
+	int res = 0;
 	odbc_obj *obj;
 	SQLINTEGER err=0, commented=0, cat_metric=0, var_metric=0, last_cat_metric=0;
 	SQLBIGINT id;
@@ -51,40 +235,16 @@ static struct ast_config *config_odbc (char *file, struct ast_config *new_config
 	int cat_started = 0;
 	int var_started = 0;
 
-	if (new_config_s) {
-		new = new_config_s;
-		cat_started++;
-	} else {
-		new = ast_new_config ();
-	}
-
-	last[0] = '\0';
 
 	if (!file || !strcmp (file, "res_config_odbc.conf"))
 		return NULL;		// cant configure myself with myself !
 
-	config = ast_load ("res_config_odbc.conf");
-
-	if (config) {
-		for (v = ast_variable_browse (config, "settings"); v; v = v->next) {
-			if (!strcmp (v->name, "table")) {
-				strncpy(table, v->value, sizeof(table) - 1);
-				configured++;
-			} else if (!strcmp (v->name, "connection")) {
-				strncpy(connection, v->value, sizeof(connection) - 1);
-				configured++;
-			}
-		}
-	ast_destroy (config);
-	}
-
-	if (configured < 2)
-		return NULL;
-
-	obj = fetch_odbc_obj (connection);
+	obj = fetch_odbc_obj(database);
 	if (!obj)
 		return NULL;
 
+	last[0] = '\0';
+
 	res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
 
 	SQLBindCol (stmt, 1, SQL_C_ULONG, &id, sizeof (id), &err);
@@ -96,7 +256,7 @@ static struct ast_config *config_odbc (char *file, struct ast_config *new_config
 	SQLBindCol (stmt, 7, SQL_C_CHAR, &var_name, sizeof (var_name), &err);
 	SQLBindCol (stmt, 8, SQL_C_CHAR, &var_val, sizeof (var_val), &err);
 
-	snprintf(sql, sizeof(sql), "select * from %s where filename='%s' and commented=0 order by filename,cat_metric desc,var_metric asc,category,var_name,var_val,id", table, file);
+	snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE filename='%s' and commented=0 ORDER BY filename,cat_metric desc,var_metric asc,category,var_name,var_val,id", table, file);
 	res = SQLExecDirect (stmt, sql, SQL_NTS);
 
 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
@@ -111,6 +271,19 @@ static struct ast_config *config_odbc (char *file, struct ast_config *new_config
 		return NULL;
 	}
 
+	if (new_config_s) {
+		new = new_config_s;
+		cat_started++;
+	} else {
+		new = ast_new_config ();
+	}
+	
+	if (!new) {
+		ast_log(LOG_WARNING, "Out of memory!\n");
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+		return NULL;
+	}
+
 	if (rowcount) {
 		res = SQLFetch (stmt);
 		cat_started = 0;
@@ -126,11 +299,7 @@ static struct ast_config *config_odbc (char *file, struct ast_config *new_config
 		while (res != SQL_NO_DATA) {
 			if (!strcmp (var_name, "#include") && recur < MAX_INCLUDE_LEVEL) {
 
-				config_odbc (var_val, new, &cur_cat, &cur_v, recur + 1
-#ifdef PRESERVE_COMMENTS
-							, acs
-#endif
-				);
+				config_odbc(database, table, var_val, new, &cur_cat, &cur_v, recur + 1);
 			} else {
 				if (strcmp (last, category) || last_cat_metric != cat_metric) {
 					strncpy(last, category, sizeof(last) - 1);
@@ -176,7 +345,8 @@ static struct ast_config *config_odbc (char *file, struct ast_config *new_config
 int unload_module (void)
 {
 	ast_cust_config_deregister (&reg1);
-	ast_log (LOG_NOTICE, "res_config_odbc unloaded.\n");
+	if (option_verbose)
+		ast_verbose("res_config_odbc unloaded.\n");
 	STANDARD_HANGUP_LOCALUSERS;
 	return 0;
 }
@@ -185,9 +355,12 @@ int load_module (void)
 {
 	memset (&reg1, 0, sizeof (struct ast_config_reg));
 	strncpy(reg1.name, "odbc", sizeof(reg1.name) - 1);
-	reg1.func = config_odbc;
+	reg1.static_func = config_odbc;
+	reg1.realtime_func = realtime_odbc;
+	reg1.update_func = update_odbc;
 	ast_cust_config_register (&reg1);
-	ast_log (LOG_NOTICE, "res_config_odbc loaded.\n");
+	if (option_verbose)
+		ast_verbose("res_config_odbc loaded.\n");
 	return 0;
 }
 
diff --git a/res/res_odbc.c b/res/res_odbc.c
index c8efb06201..c6f432ada0 100755
--- a/res/res_odbc.c
+++ b/res/res_odbc.c
@@ -1,9 +1,9 @@
 /*
  * Asterisk -- A telephony toolkit for Linux.
  *
- * Copyright (C) 1999, Mark Spencer
+ * Copyright (C) 1999-2004, Digium, Inc.
  *
- * Mark Spencer <markster@linux-support.net>
+ * Mark Spencer <markster@digium.com>
  *
  * res_odbc.c <ODBC resource manager>
  * Copyright (C) 2004 Anthony Minessale II <anthmct@yahoo.com>
@@ -45,7 +45,7 @@ static void odbc_destroy(void)
 	}
 }
 
-static odbc_obj *odbc_read(struct odbc_list *registry, char *name)
+static odbc_obj *odbc_read(struct odbc_list *registry, const char *name)
 {
 	int x = 0;
 	for (x = 0; x < MAX_ODBC_HANDLES; x++) {
@@ -124,7 +124,7 @@ static int load_odbc_config(void)
 					password = v->value;
 			}
 
-			if (enabled && dsn && username && password) {
+			if (enabled && dsn) {
 				obj = new_odbc_obj(cat, dsn, username, password);
 				if (obj) {
 					register_odbc_obj(cat, obj);
@@ -212,7 +212,7 @@ int register_odbc_obj(char *name, odbc_obj * obj)
 	return 0;
 }
 
-odbc_obj *fetch_odbc_obj(char *name)
+odbc_obj *fetch_odbc_obj(const char *name)
 {
 	return (odbc_obj *) odbc_read(ODBC_REGISTRY, name);
 }
@@ -233,18 +233,22 @@ odbc_obj *new_odbc_obj(char *name, char *dsn, char *username, char *password)
 	if (new->dsn == NULL)
 		return NULL;
 
-	new->username = malloc(strlen(username) + 1);
-	if (new->username == NULL)
-		return NULL;
+	if (username) {
+		new->username = malloc(strlen(username) + 1);
+		if (new->username == NULL)
+			return NULL;
+		strcpy(new->username, username);
+	}
 
-	new->password = malloc(strlen(password) + 1);
-	if (new->password == NULL)
-		return NULL;
+	if (password) {
+		new->password = malloc(strlen(password) + 1);
+		if (new->password == NULL)
+			return NULL;
+		strcpy(new->password, password);
+	}
 
 	strcpy(new->name, name);
 	strcpy(new->dsn, dsn);
-	strcpy(new->username, username);
-	strcpy(new->password, password);
 	new->up = 0;
 	ast_mutex_init(&new->lock);
 	return new;
@@ -261,8 +265,10 @@ void destroy_obdc_obj(odbc_obj ** obj)
 
 	free((*obj)->name);
 	free((*obj)->dsn);
-	free((*obj)->username);
-	free((*obj)->password);
+	if ((*obj)->username)
+		free((*obj)->username);
+	if ((*obj)->password)
+		free((*obj)->password);
 	ast_mutex_unlock(&(*obj)->lock);
 	free(*obj);
 }
@@ -332,6 +338,7 @@ odbc_status odbc_obj_connect(odbc_obj * obj)
 		SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
 	}
 
+	ast_log(LOG_NOTICE, "Calling %p/%p\n", obj->username, obj->password);
 	res = SQLConnect(obj->con,
 		   (SQLCHAR *) obj->dsn, SQL_NTS,
 		   (SQLCHAR *) obj->username, SQL_NTS,
@@ -340,13 +347,11 @@ odbc_status odbc_obj_connect(odbc_obj * obj)
 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
 		SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
 		SQLFreeHandle(SQL_HANDLE_ENV, obj->env);
-		if (option_verbose > 3)
-			ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%ld %s\n", res, err, msg);
+		ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%ld %s\n", res, err, msg);
 		return ODBC_FAIL;
 	} else {
 
-		if (option_verbose > 3)
-			ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->name, obj->dsn);
+		ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->name, obj->dsn);
 		obj->up = 1;
 	}
 
diff --git a/utils.c b/utils.c
index 2557c3180e..a454ea4bf9 100755
--- a/utils.c
+++ b/utils.c
@@ -28,6 +28,19 @@
 static char base64[64];
 static char b2a[256];
 
+char *ast_strip(char *buf)
+{
+	char *start;
+	/* Strip off trailing whitespace, returns, etc */
+	while (!ast_strlen_zero(buf) && (buf[strlen(buf)-1]<33))
+		buf[strlen(buf)-1] = '\0';
+	start = buf;
+	/* Strip off leading whitespace, returns, etc */
+	while (*start && (*start < 33))
+		*start++ = '\0';
+	return start;
+}
+
 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__)
 
 /* duh? ERANGE value copied from web... */
-- 
GitLab