diff --git a/apps/Makefile b/apps/Makefile
index a50ac8091d6410898c0e9535d722742d2116ba17..08e24de11938639b04187fb0250537207ad6fce0 100755
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -50,14 +50,11 @@ APPS+=$(shell if [ -f /usr/local/include/zaptel.h ]; then echo "app_zapras.so ap
 APPS+=$(shell if [ -f /usr/include/osp/osp.h ]; then echo "app_osplookup.so" ; fi)
 
 CFLAGS+=-fPIC
-
-ifeq ($(USE_POSTGRES_VM_INTERFACE),1)
-CFLAGS+=-DUSEPOSTGRESVM
-endif
-
-ifeq ($(USE_MYSQL_VM_INTERFACE),1)
-CFLAGS+=-DUSEMYSQLVM
-endif
+#
+# If you have MySQL 4.1 or later you can use ODBC
+# storage
+#
+#CFLAGS+=-DUSE_ODBC_STORAGE
 
 all: $(APPS)
 
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index bfe4ddb8b4dd9b34cf6cd63447fa6abbb496c179..61702c2ae5a4ba2ad5763fa76303f34c967af629 100755
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -28,6 +28,9 @@
 #include <asterisk/localtime.h>
 #include <asterisk/cli.h>
 #include <asterisk/utils.h>
+#ifdef USE_ODBC_STORAGE
+#include <asterisk/res_odbc.h>
+#endif
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
@@ -37,6 +40,7 @@
 #include <sys/time.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/mman.h>
 #include <time.h>
 #include <dirent.h>
 
@@ -175,9 +179,27 @@ struct vm_state {
 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration);
-static int vm_delete(char *file);
 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc);
 
+#ifdef USE_ODBC_STORAGE
+static char odbc_database[80];
+#define RETRIEVE(a,b) retrieve_file(a,b)
+#define DISPOSE(a,b) remove_file(a,b)
+#define STORE(a,b) store_file(a,b)
+#define EXISTS(a,b,c,d) (message_exists(a,b))
+#define RENAME(a,b,c,d,e,f) (rename_file(a,b,c,d))
+#define COPY(a,b,c,d,e,f) (copy_file(a,b,c,d))
+#define DELETE(a,b,c) (delete_file(a,b))
+#else
+#define RETRIEVE(a,b)
+#define DISPOSE(a,b)
+#define STORE(a,b)
+#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
+#define RENAME(a,b,c,d,e,f) (rename_file(e,f));
+#define COPY(a,b,c,d,e,f) (copy_file(e,f));
+#define DELETE(a,b,c) (vm_delete(c))
+#endif
+
 static char ext_pass_cmd[128];
 
 static char *tdesc = "Comedian Mail (Voicemail System)";
@@ -664,6 +686,699 @@ static int make_file(char *dest, int len, char *dir, int num)
 	return snprintf(dest, len, "%s/msg%04d", dir, num);
 }
 
+
+#ifdef USE_ODBC_STORAGE
+static int retrieve_file(char *dir, int msgnum)
+{
+	int x = 0;
+	int res;
+	int fd=-1;
+	size_t fdlen = 0;
+	void *fdm=NULL;
+	SQLLEN rowcount=0;
+	SQLSMALLINT colcount=0;
+	SQLHSTMT stmt;
+	char sql[256];
+	char rdir[256];
+	char fmt[80]="";
+	char *c;
+	char coltitle[256];
+	SQLSMALLINT collen;
+	SQLSMALLINT datatype;
+	SQLSMALLINT decimaldigits;
+	SQLSMALLINT nullable;
+	SQLULEN colsize;
+	FILE *f=NULL;
+	char rowdata[80];
+	char fn[256];
+	char full_fn[256];
+	char msgnums[80];
+	
+	odbc_obj *obj;
+	obj = fetch_odbc_obj(odbc_database);
+	if (obj) {
+		if (dir[0] != '/') {
+			snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
+			dir = rdir;
+		}
+		strncpy(fmt, vmfmts, sizeof(fmt) - 1);
+		c = strchr(fmt, '|');
+		if (c)
+			*c = '\0';
+		if (!strcasecmp(fmt, "wav49"))
+			strncpy(fmt, "WAV", sizeof(fmt));
+		snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+		if (msgnum > -1)
+			make_file(fn, sizeof(fn), dir, msgnum);
+		else
+			strncpy(fn, dir, sizeof(fn) - 1);
+		snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+		f = fopen(full_fn, "w+");
+		snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+		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");
+			goto yuck;
+		}
+		snprintf(sql, sizeof(sql), "SELECT * FROM voicemessages WHERE dir=? AND msgnum=?");
+		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);
+			goto yuck;
+		}
+		SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
+		SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 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);
+			goto yuck;
+		}
+		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);
+			goto yuck;
+		}
+		if (rowcount) {
+			fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
+			if (fd < 0) {
+				ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
+				SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+				goto yuck;
+			}
+			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);
+				goto yuck;
+			}
+			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);
+				goto yuck;
+			}
+			if (f) 
+				fprintf(f, "[message]\n");
+			for (x=0;x<colcount;x++) {
+				rowdata[0] = '\0';
+				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);
+					SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+					goto yuck;
+				}
+				if (!strcmp(coltitle, "recording")) {
+					fdlen = colsize;
+					fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
+					if (fd > -1) {
+						/* Ugh, gotta fill it  so we can mmap */
+						char tmp[1024]="";
+						size_t left = 0, bytes = 0;
+						left = fdlen;
+						while(left) {
+							bytes = left;
+							if (bytes > sizeof(tmp))
+								bytes = sizeof(tmp);
+							if (write(fd, tmp, bytes) != bytes) {
+								close(fd); 
+								fd = -1;
+								break;
+							}
+							left -= bytes;
+						}
+						if (fd > -1)
+							fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+					}
+					if (fdm) {
+						memset(fdm, 0, fdlen);
+						res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
+						if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+							ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+							SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+							goto yuck;
+						}
+					}
+				} else {
+					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);
+						SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+						goto yuck;
+					}
+					printf("Got field '%s'\n", coltitle);
+					if (strcmp(coltitle, "msgnum") && strcmp(coltitle, "dir") && f)
+						fprintf(f, "%s=%s\n", coltitle, rowdata);
+				}
+			}
+		}
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	} else
+		ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:	
+	if (f)
+		fclose(f);
+	if (fdm)
+		munmap(fdm, fdlen);
+	if (fd > -1)
+		close(fd);
+	return x - 1;
+}
+
+static int remove_file(char *dir, int msgnum)
+{
+	char fn[256];
+	char full_fn[256];
+	char msgnums[80];
+	char rdir[256];
+	
+	if (dir[0] != '/') {
+		snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
+		dir = rdir;
+	}
+	if (msgnum > -1) {
+		snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+		make_file(fn, sizeof(fn), dir, msgnum);
+	} else
+		strncpy(fn, dir, sizeof(fn) - 1);
+	ast_filedelete(fn, NULL);	
+	snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+	unlink(full_fn);
+	return 0;
+}
+
+static int last_message_index(char *dir)
+{
+	int x = 0;
+	int res;
+	SQLLEN rowcount=0;
+	SQLHSTMT stmt;
+	char sql[256];
+	char rdir[256];
+	char rowdata[20];
+	
+	odbc_obj *obj;
+	obj = fetch_odbc_obj(odbc_database);
+	if (obj) {
+		if (dir[0] != '/') {
+			snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
+			dir = rdir;
+		}
+		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");
+			goto yuck;
+		}
+		snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=?");
+		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);
+			goto yuck;
+		}
+		SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 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);
+			goto yuck;
+		}
+		res = SQLRowCount(stmt, &rowcount);
+		if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
+			ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			goto yuck;
+		}
+		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);
+			goto yuck;
+		}
+		res = SQLGetData(stmt, 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);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			goto yuck;
+		}
+		if (sscanf(rowdata, "%i", &x) != 1)
+			ast_log(LOG_WARNING, "Failed to read message count!\n");
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	} else
+		ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:	
+	return x - 1;
+}
+
+static int message_exists(char *dir, int msgnum)
+{
+	int x = 0;
+	int res;
+	SQLLEN rowcount=0;
+	SQLHSTMT stmt;
+	char sql[256];
+	char rdir[256];
+	char rowdata[20];
+	char msgnums[20];
+	
+	odbc_obj *obj;
+	obj = fetch_odbc_obj(odbc_database);
+	if (obj) {
+		if (dir[0] != '/') {
+			snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
+			dir = rdir;
+		}
+		snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+		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");
+			goto yuck;
+		}
+		snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=? AND msgnum=?");
+		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);
+			goto yuck;
+		}
+		SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
+		SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 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);
+			goto yuck;
+		}
+		res = SQLRowCount(stmt, &rowcount);
+		if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
+			ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			goto yuck;
+		}
+		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);
+			goto yuck;
+		}
+		res = SQLGetData(stmt, 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);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			goto yuck;
+		}
+		if (sscanf(rowdata, "%i", &x) != 1)
+			ast_log(LOG_WARNING, "Failed to read message count!\n");
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	} else
+		ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:	
+	return x;
+}
+
+static int count_messages(char *dir)
+{
+	return last_message_index(dir) + 1;
+}
+static void delete_file(char *sdir, int smsg)
+{
+	int res;
+	SQLLEN rowcount=0;
+	SQLHSTMT stmt;
+	char sql[256];
+	char msgnums[20];
+	char rdir[256];
+	
+	odbc_obj *obj;
+	obj = fetch_odbc_obj(odbc_database);
+	if (obj) {
+		if (sdir[0] != '/') {
+			snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
+			sdir = rdir;
+		}
+		snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+		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");
+			goto yuck;
+		}
+		snprintf(sql, sizeof(sql), "DELETE FROM voicemessages WHERE dir=? AND msgnum=?");
+		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);
+			goto yuck;
+		}
+		SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
+		SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 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);
+			goto yuck;
+		}
+		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);
+			goto yuck;
+		}
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	} else
+		ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+	return;	
+}
+
+static void copy_file(char *sdir, int smsg, char *ddir, int dmsg)
+{
+	int res;
+	SQLLEN rowcount=0;
+	SQLHSTMT stmt;
+	char sql[256];
+	char msgnums[20];
+	char msgnumd[20];
+	char rsdir[256];
+	char rddir[256];
+	odbc_obj *obj;
+
+	delete_file(ddir, dmsg);
+	obj = fetch_odbc_obj(odbc_database);
+	if (obj) {
+		if (ddir[0] != '/') {
+			snprintf(rddir, sizeof(rddir), "%s/%s", ast_config_AST_SPOOL_DIR, ddir);
+			ddir = rddir;
+		}
+		if (sdir[0] != '/') {
+			snprintf(rsdir, sizeof(rsdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
+			sdir = rsdir;
+		}
+		snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+		snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+		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");
+			goto yuck;
+		}
+		snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording FROM voicemessages WHERE dir=? AND msgnum=?");
+		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);
+			goto yuck;
+		}
+		SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
+		SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
+		SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
+		SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+		res = SQLExecute(stmt);
+		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+			ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			goto yuck;
+		}
+		res = SQLRowCount(stmt, &rowcount);
+		if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
+			ast_log(LOG_WARNING, "SQL Row Count error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			goto yuck;
+		}
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	} else
+		ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+	return;	
+}
+
+static int store_file(char *dir, int msgnum)
+{
+	int x = 0;
+	int res;
+	int fd = -1;
+	void *fdm=NULL;
+	size_t fdlen = -1;
+	SQLLEN rowcount=0;
+	SQLHSTMT stmt;
+	SQLINTEGER len;
+	char sql[256];
+	char rdir[256];
+	char msgnums[20];
+	char fn[256];
+	char full_fn[256];
+	char fmt[80]="";
+	char *c;
+	char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
+	struct ast_config *cfg=NULL;
+	odbc_obj *obj;
+
+	delete_file(dir, msgnum);
+	obj = fetch_odbc_obj(odbc_database);
+	if (obj) {
+		if (dir[0] != '/') {
+			snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
+			dir = rdir;
+		}
+		strncpy(fmt, vmfmts, sizeof(fmt) - 1);
+		c = strchr(fmt, '|');
+		if (c)
+			*c = '\0';
+		if (!strcasecmp(fmt, "wav49"))
+			strncpy(fmt, "WAV", sizeof(fmt));
+		snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+		if (msgnum > -1)
+			make_file(fn, sizeof(fn), dir, msgnum);
+		else
+			strncpy(fn, dir, sizeof(fn) - 1);
+		snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+		cfg = ast_load(full_fn);
+		snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+		fd = open(full_fn, O_RDWR);
+		if (fd < 0) {
+			ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
+			goto yuck;
+		}
+		if (cfg) {
+			context = ast_variable_retrieve(cfg, "message", "context");
+			if (!context) context = "";
+			macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
+			if (!macrocontext) macrocontext = "";
+			callerid = ast_variable_retrieve(cfg, "message", "callerid");
+			if (!callerid) callerid = "";
+			origtime = ast_variable_retrieve(cfg, "message", "origtime");
+			if (!origtime) origtime = "";
+			duration = ast_variable_retrieve(cfg, "message", "duration");
+			if (!duration) duration = "";
+		}
+		fdlen = lseek(fd, 0, SEEK_END);
+		lseek(fd, 0, SEEK_SET);
+		printf("Length is %d\n", fdlen);
+		fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
+		if (!fdm) {
+			ast_log(LOG_WARNING, "Memory map failed!\n");
+			goto yuck;
+		} 
+		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");
+			goto yuck;
+		}
+		snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)");
+		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);
+			goto yuck;
+		}
+		len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
+		SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
+		SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+		SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
+		SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
+		SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
+		SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
+		SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
+		SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 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);
+			goto yuck;
+		}
+		res = SQLRowCount(stmt, &rowcount);
+		if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
+			ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			goto yuck;
+		}
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	} else
+		ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:	
+	if (cfg)
+		ast_destroy(cfg);
+	if (fdm)
+		munmap(fdm, fdlen);
+	if (fd > -1)
+		close(fd);
+	return x;
+}
+
+static void rename_file(char *sdir, int smsg, char *ddir, int dmsg)
+{
+	int res;
+	SQLLEN rowcount=0;
+	SQLHSTMT stmt;
+	char sql[256];
+	char msgnums[20];
+	char msgnumd[20];
+	char rsdir[256];
+	char rddir[256];
+	odbc_obj *obj;
+
+	delete_file(ddir, dmsg);
+	obj = fetch_odbc_obj(odbc_database);
+	if (obj) {
+		if (ddir[0] != '/') {
+			snprintf(rddir, sizeof(rddir), "%s/%s", ast_config_AST_SPOOL_DIR, ddir);
+			ddir = rddir;
+		}
+		if (sdir[0] != '/') {
+			snprintf(rsdir, sizeof(rsdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
+			sdir = rsdir;
+		}
+		snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+		snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+		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");
+			goto yuck;
+		}
+		snprintf(sql, sizeof(sql), "UPDATE voicemessages SET dir=?, msgnum=? WHERE dir=? AND msgnum=?");
+		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);
+			goto yuck;
+		}
+		SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
+		SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
+		SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
+		SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 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);
+			goto yuck;
+		}
+		res = SQLRowCount(stmt, &rowcount);
+		if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
+			ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			goto yuck;
+		}
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	} else
+		ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+	return;	
+}
+
+#else
+static int count_messages(char *dir)
+{
+	// Find all .txt files - even if they are not in sequence from 0000
+
+	int vmcount = 0;
+	DIR *vmdir = NULL;
+	struct dirent *vment = NULL;
+
+	if ((vmdir = opendir(dir))) {
+		while ((vment = readdir(vmdir)))
+		{
+			if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
+			{
+				vmcount++;
+			}
+		}
+		closedir(vmdir);
+	}
+
+	return vmcount;
+}
+
+static void rename_file(char *sfn, char *dfn)
+{
+	char stxt[256];
+	char dtxt[256];
+	ast_filerename(sfn,dfn,NULL);
+	snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
+	snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
+	rename(stxt, dtxt);
+}
+
+static int copy(char *infile, char *outfile)
+{
+	int ifd;
+	int ofd;
+	int res;
+	int len;
+	char buf[4096];
+
+#ifdef HARDLINK_WHEN_POSSIBLE
+	/* Hard link if possible; saves disk space & is faster */
+	if (link(infile, outfile)) {
+#endif
+		if ((ifd = open(infile, O_RDONLY)) < 0) {
+			ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
+			return -1;
+		}
+		if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
+			ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
+			close(ifd);
+			return -1;
+		}
+		do {
+			len = read(ifd, buf, sizeof(buf));
+			if (len < 0) {
+				ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+				close(ifd);
+				close(ofd);
+				unlink(outfile);
+			}
+			if (len) {
+				res = write(ofd, buf, len);
+				if (res != len) {
+					ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+					close(ifd);
+					close(ofd);
+					unlink(outfile);
+				}
+			}
+		} while (len);
+		close(ifd);
+		close(ofd);
+		return 0;
+#ifdef HARDLINK_WHEN_POSSIBLE
+	} else {
+		/* Hard link succeeded */
+		return 0;
+	}
+#endif
+}
+
+static void copy_file(char *frompath, char *topath)
+{
+	char frompath2[256],topath2[256];
+	ast_filecopy(frompath, topath, NULL);
+	snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
+	snprintf(topath2, sizeof(topath2), "%s.txt", topath);
+	copy(frompath2, topath2);
+}
+
 static int last_message_index(char *dir)
 {
 	int x;
@@ -676,6 +1391,23 @@ static int last_message_index(char *dir)
 	return x-1;
 }
 
+static int vm_delete(char *file)
+{
+	char *txt;
+	int txtsize = 0;
+
+	txtsize = (strlen(file) + 5)*sizeof(char);
+	txt = (char *)alloca(txtsize);
+	/* Sprintf here would safe because we alloca'd exactly the right length,
+	 * but trying to eliminate all sprintf's anyhow
+	 */
+	snprintf(txt, txtsize, "%s.txt", file);
+	unlink(txt);
+	return ast_filedelete(file, NULL);
+}
+
+
+#endif
 static int
 inbuf(struct baseio *bio, FILE *fi)
 {
@@ -1070,14 +1802,21 @@ static int invent_message(struct ast_channel *chan, char *context, char *ext, in
 	int res;
 	char fn[256];
 	snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
+	RETRIEVE(fn, -1);
 	if (ast_fileexists(fn, NULL, NULL) > 0) {
 		res = ast_streamfile(chan, fn, chan->language);
-		if (res)
+		if (res) {
+			DISPOSE(fn, -1);
 			return -1;
+		}
 		res = ast_waitstream(chan, ecodes);
-		if (res)
+		if (res) {
+			DISPOSE(fn, -1);
 			return res;
+		}
 	} else {
+		/* Dispose just in case */
+		DISPOSE(fn, -1);
 		res = ast_streamfile(chan, "vm-theperson", chan->language);
 		if (res)
 			return -1;
@@ -1137,56 +1876,6 @@ static char *mbox(int id)
 	}
 }
 
-static int copy(char *infile, char *outfile)
-{
-	int ifd;
-	int ofd;
-	int res;
-	int len;
-	char buf[4096];
-
-#ifdef HARDLINK_WHEN_POSSIBLE
-	/* Hard link if possible; saves disk space & is faster */
-	if (link(infile, outfile)) {
-#endif
-		if ((ifd = open(infile, O_RDONLY)) < 0) {
-			ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
-			return -1;
-		}
-		if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
-			ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
-			close(ifd);
-			return -1;
-		}
-		do {
-			len = read(ifd, buf, sizeof(buf));
-			if (len < 0) {
-				ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
-				close(ifd);
-				close(ofd);
-				unlink(outfile);
-			}
-			if (len) {
-				res = write(ofd, buf, len);
-				if (res != len) {
-					ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
-					close(ifd);
-					close(ofd);
-					unlink(outfile);
-				}
-			}
-		} while (len);
-		close(ifd);
-		close(ofd);
-		return 0;
-#ifdef HARDLINK_WHEN_POSSIBLE
-	} else {
-		/* Hard link succeeded */
-		return 0;
-	}
-#endif
-}
-
 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
 
 static void copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt)
@@ -1214,16 +1903,12 @@ static void copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int
 	recipmsgnum = 0;
 	do {
 		make_file(topath, sizeof(topath), todir, recipmsgnum);
-		if (ast_fileexists(topath, NULL, chan->language) <= 0) 
+		if (!EXISTS(todir, recipmsgnum, topath, chan->language))
 			break;
 		recipmsgnum++;
 	} while (recipmsgnum < MAXMSG);
 	if (recipmsgnum < MAXMSG) {
-		char frompath2[256],topath2[256];
-		ast_filecopy(frompath, topath, NULL);
-		snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
-		snprintf(topath2, sizeof(topath2), "%s.txt", topath);
-		copy(frompath2, topath2);
+		COPY(fromdir, msgnum, todir, recipmsgnum, frompath, topath);
 	} else {
 		ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
 	}
@@ -1298,6 +1983,11 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
 			snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
 		else if (unavail)
 			snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
+		snprintf(tempfile, sizeof(tempfile), "voicemail/%s/%s/temp", vmu->context, ext);
+		RETRIEVE(tempfile, -1);
+		if (ast_fileexists(tempfile, NULL, NULL) > 0)
+			strncpy(prefile, tempfile, sizeof(prefile) - 1);
+		DISPOSE(tempfile, -1);
 		make_dir(dir, sizeof(dir), vmu->context, "", "");
 		/* It's easier just to try to make it than to check for its existence */
 		if (mkdir(dir, 0700) && (errno != EEXIST))
@@ -1331,12 +2021,10 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
 			ausemacro = 1;
 		}
 
-		snprintf(tempfile, sizeof(tempfile), "voicemail/%s/%s/temp", vmu->context, ext);
 
 		/* Play the beginning intro if desired */
 		if (!ast_strlen_zero(prefile)) {
-			if (ast_fileexists(tempfile, NULL, NULL) > 0)
-				strncpy(prefile, tempfile, sizeof(prefile) - 1);
+			RETRIEVE(prefile, -1);
 			if (ast_fileexists(prefile, NULL, NULL) > 0) {
 				if (ast_streamfile(chan, prefile, chan->language) > -1) 
 					res = ast_waitstream(chan, ecodes);
@@ -1344,6 +2032,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
 				ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
 				res = invent_message(chan, vmu->context, ext, busy, ecodes);
 			}
+			DISPOSE(prefile, -1);
 			if (res < 0) {
 				ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
 				free_user(vmu);
@@ -1408,7 +2097,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
 			msgnum = 0;
 			do {
 				make_file(fn, sizeof(fn), dir, msgnum);
-				if (ast_fileexists(fn, NULL, chan->language) <= 0) 
+				if (!EXISTS(dir,msgnum,fn,chan->language))
 					break;
 				msgnum++;
 			} while (msgnum < MAXMSG);
@@ -1466,7 +2155,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
 				if (duration < vmminmessage) {
 					if (option_verbose > 2) 
 						ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
-					vm_delete(fn);
+					DELETE(dir,msgnum,fn);
 					/* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
 					goto leave_vm_out;
 				}
@@ -1487,6 +2176,8 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
 					}
 				}
 				notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
+				STORE(dir, msgnum);
+				DISPOSE(dir, msgnum);
 			} else {
 				res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
 				if (!res)
@@ -1507,27 +2198,8 @@ leave_vm_out:
 	return res;
 }
 
-static int count_messages(char *dir)
-{
-	// Find all .txt files - even if they are not in sequence from 0000
-
-	int vmcount = 0;
-	DIR *vmdir = NULL;
-	struct dirent *vment = NULL;
-
-	if ((vmdir = opendir(dir))) {
-		while ((vment = readdir(vmdir)))
-		{
-			if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
-			{
-				vmcount++;
-			}
-		}
-		closedir(vmdir);
-	}
-
-	return vmcount;
-}
+#ifdef USE_ODBC_STORAGE
+#endif
 
 static void resequence_mailbox(char * dir)
 {
@@ -1536,20 +2208,14 @@ static void resequence_mailbox(char * dir)
 	int x,dest;
 	char sfn[256];
 	char dfn[256];
-	char stxt[256];
-	char dtxt[256];
 
 	for (x=0,dest=0;x<MAXMSG;x++) {
 		make_file(sfn, sizeof(sfn), dir, x);
-		if (ast_fileexists(sfn, NULL, NULL) > 0) {
+		if (EXISTS(dir, x, sfn, NULL)) {
 
 			if(x != dest) {
 				make_file(dfn, sizeof(dfn), dir, dest);
-				ast_filerename(sfn,dfn,NULL);
-
-				snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
-				snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
-				rename(stxt, dtxt);
+				RENAME(dir, x, dir, dest, sfn, dfn);
 			}
 
 			dest++;
@@ -1570,8 +2236,6 @@ static int save_to_folder(char *dir, int msg, char *context, char *username, int
 	char sfn[256];
 	char dfn[256];
 	char ddir[256];
-	char txt[256];
-	char ntxt[256];
 	char *dbox = mbox(box);
 	int x;
 	make_file(sfn, sizeof(sfn), dir, msg);
@@ -1579,16 +2243,13 @@ static int save_to_folder(char *dir, int msg, char *context, char *username, int
 	mkdir(ddir, 0700);
 	for (x=0;x<MAXMSG;x++) {
 		make_file(dfn, sizeof(dfn), ddir, x);
-		if (ast_fileexists(dfn, NULL, NULL) < 0)
+		if (!EXISTS(ddir, x, dfn, NULL))
 			break;
 	}
 	if (x >= MAXMSG)
 		return -1;
-	ast_filecopy(sfn, dfn, NULL);
 	if (strcmp(sfn, dfn)) {
-		snprintf(txt, sizeof(txt), "%s.txt", sfn);
-		snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
-		copy(txt, ntxt);
+		COPY(dir, msg, ddir, x, sfn, dfn);
 	}
 	return 0;
 }
@@ -2213,7 +2874,7 @@ static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu,
 	}
 
 	if (vmu->delete) {
-		vm_delete(fn);
+		DELETE(todir, msgnum, fn);
 	}
 
 	/* Leave voicemail for someone */
@@ -2630,14 +3291,18 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc
 	/* Retrieve info from VM attribute file */
 	make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
 	snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
+	RETRIEVE(vms->curdir, vms->curmsg);
 	msg_cfg = ast_load(filename);
 	if (!msg_cfg) {
 		ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
 		return 0;
 	}
 																									
-	if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
+	if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
+		ast_log(LOG_WARNING, "No origtime?!\n");
+		DISPOSE(vms->curdir, vms->curmsg);
 		return 0;
+	}
 
 	cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
 	duration = ast_variable_retrieve(msg_cfg, "message", "duration");
@@ -2660,8 +3325,10 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc
 	if (!res) {
 		make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
 		vms->heard[vms->curmsg] = 1;
+		printf("yay!\n");
 		res = wait_file(chan, vms, vms->fn);
 	}
+	DISPOSE(vms->curdir, vms->curmsg);
 	return res;
 }
 
@@ -2690,8 +3357,6 @@ static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
 {
 	int x;
-	char ntxt[256] = "";
-	char txt[256] = "";
 	if (vms->lastmsg > -1) { 
 		/* Get the deleted messages fixed */ 
 		vms->curmsg = -1; 
@@ -2699,15 +3364,12 @@ static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
 			if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
 				/* Save this message.  It's not in INBOX or hasn't been heard */ 
 				make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
-				if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
+				if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
 					break;
 				vms->curmsg++; 
 				make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
 				if (strcmp(vms->fn, vms->fn2)) { 
-					snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
-					snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
-					ast_filerename(vms->fn, vms->fn2, NULL); 
-					rename(txt, ntxt); 
+					RENAME(vms->curdir, x, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
 				} 
 			} else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
 				/* Move to old folder before deleting */ 
@@ -2716,9 +3378,9 @@ static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
 		} 
 		for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
 			make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
-			if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
+			if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
 				break;
-			vm_delete(vms->fn);
+			DELETE(vms->curdir, x, vms->fn);
 		} 
 	} 
 	memset(vms->deleted, 0, sizeof(vms->deleted)); 
@@ -4335,6 +4997,12 @@ static int load_config(void)
 			astattach = "yes";
 		attach_voicemail = ast_true(astattach);
 
+#ifdef USE_ODBC_STORAGE
+		strncpy(odbc_database, "asterisk", sizeof(odbc_database) - 1);
+		if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
+			strncpy(odbc_database, thresholdstr, sizeof(odbc_database) - 1);
+		}
+#endif		
 		/* Mail command */
 		strncpy(mailcmd, SENDMAIL, sizeof(mailcmd) - 1); /* Default */
 		if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
@@ -4947,6 +5615,8 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
  				ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
  				ast_streamfile(chan, "vm-msgsaved", chan->language);
  				ast_waitstream(chan, "");
+				STORE(recordfile, -1);
+				DISPOSE(recordfile, -1);
  				cmd = 't';
  				return res;
  			}
@@ -4970,9 +5640,10 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
  			recorded = 1;
  			/* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
 			cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence);
- 			if (cmd == -1)
+ 			if (cmd == -1) {
  			/* User has hung up, no options to give */
  				return cmd;
+			}
  			if (cmd == '0') {
  				break;
  			} else if (cmd == '*') {
@@ -5029,7 +5700,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
  		case '0':
 			if (message_exists || recorded) {
 				ast_play_and_wait(chan, "vm-deleted");
-				vm_delete(recordfile);
+				DELETE(recordfile, -1, recordfile);
 			}
 			return cmd;
  		default:
@@ -5074,21 +5745,6 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
  }
  
 
-static int vm_delete(char *file)
-{
-	char *txt;
-	int txtsize = 0;
-
-	txtsize = (strlen(file) + 5)*sizeof(char);
-	txt = (char *)alloca(txtsize);
-	/* Sprintf here would safe because we alloca'd exactly the right length,
-	 * but trying to eliminate all sprintf's anyhow
-	 */
-	snprintf(txt, txtsize, "%s.txt", file);
-	unlink(txt);
-	return ast_filedelete(file, NULL);
-}
-
 int usecount(void)
 {
 	int res;
diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c
index e00eecd4465106d2026c63eff3eb000eafa56507..d0eb84478aa04a447920d5747df9de42245f71c0 100755
--- a/channels/chan_alsa.c
+++ b/channels/chan_alsa.c
@@ -588,13 +588,13 @@ static int alsa_write(struct ast_channel *chan, struct ast_frame *f)
 		} else {
 			if (res == -ESTRPIPE) {
 				ast_log(LOG_ERROR, "You've got some big problems\n");
-			}
-			if (res > 0)
-				res = 0;
+			} else if (res < 0)
+				ast_log(LOG_NOTICE, "Error %d on write\n", res);
 		}
 	}
 	ast_mutex_unlock(&alsalock);
-
+	if (res > 0)
+		res = 0;
 	return res;
 }
 
diff --git a/configs/extconfig.conf.sample b/configs/extconfig.conf.sample
index 20b2b9ad28510fc0944b4803123f38ce0e144d4e..43de48f8bb6f1997eba675c368fd7dc275a1c430 100755
--- a/configs/extconfig.conf.sample
+++ b/configs/extconfig.conf.sample
@@ -30,4 +30,5 @@
 ;iaxfriends => odbc,asterisk
 ;sipfriends => odbc,asterisk
 ;voicemail => odbc,asterisk
+;extensions => odbc,asterisk
 
diff --git a/configs/res_odbc.conf.sample b/configs/res_odbc.conf.sample
index 144eb6a95b795e380715d90335967d609a41003c..6b162a644927348c3a3fa05f03eacbdd92e788c8 100755
--- a/configs/res_odbc.conf.sample
+++ b/configs/res_odbc.conf.sample
@@ -1,17 +1,17 @@
 ;;; odbc setup file 
 
-[mysql1]
-dsn => MySQL-asterisk
-username => myuser
-password => mypass
+[asterisk]
+dsn => asterisk
+;username => myuser
+;password => mypass
 pre-connect => yes
 
 
-[mysql2]
-dsn => MySQL-asterisk
-username => myuser
-password => mypass
-pre-connect => yes
+;[mysql2]
+;dsn => MySQL-asterisk
+;username => myuser
+;password => mypass
+;pre-connect => yes
 
 
 
diff --git a/doc/README.extconfig b/doc/README.extconfig
index 85d4dd2c171c784c8c37891745f906fb11ec75fd..e26ab6c3a37c3569d5c5763ad577260ceb80df1a 100755
--- a/doc/README.extconfig
+++ b/doc/README.extconfig
@@ -58,3 +58,21 @@ A Voicemail table would look more like this:
 The uniqueid should be unique to each voicemail user and can be 
 autoincrement.  It need not have any relation to the mailbox or context.
 
+An extension table would look more like this:
+
++----------+---------+----------+-------+-----------+
+| context  |  exten  | priority |  app  |  appdata  |
++----------+---------+----------+-------+-----------+
+|  default |    1234 |        1 |  Dial |     Zap/1 |
++----------+---------+----------+-------+-----------+
+
+In the dialplan you just use the Realtime switch:
+
+[foo]
+switch => Realtime
+
+or:
+
+[bar]
+switch => Realtime/bar@extensions
+