Index: build_tools/menuselect-deps.in
===================================================================
--- build_tools/menuselect-deps.in	(revision 220097)
+++ build_tools/menuselect-deps.in	(working copy)
@@ -27,6 +27,7 @@
 SPEEXDSP=@PBX_SPEEXDSP@
 SPEEX_PREPROCESS=@PBX_SPEEX_PREPROCESS@
 SQLITE=@PBX_SQLITE@
+SQLITE3=@PBX_SQLITE3@
 SSL=@PBX_OPENSSL@
 TONEZONE=@PBX_TONEZONE@
 USB=@PBX_USB@
Index: configure.ac
===================================================================
--- configure.ac	(revision 220097)
+++ configure.ac	(working copy)
@@ -211,6 +211,7 @@
 AST_EXT_LIB_SETUP([SPEEXDSP], [Speexdsp], [speexdsp])
 AST_EXT_LIB_SETUP_DEPENDENT([SPEEX_PREPROCESS], [speex_preprocess_ctl], [], [speex])
 AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite])
+AST_EXT_LIB_SETUP([SQLITE3], [SQLite], [sqlite3])
 AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv])
 AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL], [ssl])
 AST_EXT_LIB_SETUP([FREETDS], [FreeTDS], [tds])
@@ -1379,6 +1380,8 @@
 
 AST_EXT_LIB_CHECK([SQLITE], [sqlite], [sqlite_exec], [sqlite.h])
 
+AST_EXT_LIB_CHECK([SQLITE3], [sqlite3], [sqlite3_open], [sqlite3.h])
+
 AST_EXT_LIB_CHECK([OPENSSL], [ssl], [ssl2_connect], [openssl/ssl.h], [-lcrypto])
 if test "$PBX_OPENSSL" = "1";
 then
Index: include/asterisk/autoconfig.h.in
===================================================================
--- include/asterisk/autoconfig.h.in	(revision 220097)
+++ include/asterisk/autoconfig.h.in	(working copy)
@@ -374,6 +374,9 @@
 /* Define to 1 if you have the SQLite library. */
 #undef HAVE_SQLITE
 
+/* Define to indicate the ${SQLITE3_DESCRIP} library */
+#undef HAVE_SQLITE3
+
 /* Define to 1 if you have the `sqrt' function. */
 #undef HAVE_SQRT
 
Index: makeopts.in
===================================================================
--- makeopts.in	(revision 220097)
+++ makeopts.in	(working copy)
@@ -150,6 +150,9 @@
 SQLITE_INCLUDE=@SQLITE_INCLUDE@
 SQLITE_LIB=@SQLITE_LIB@
 
+SQLITE3_INCLUDE=@SQLITE3_INCLUDE@
+SQLITE3_LIB=@SQLITE3_LIB@
+
 SSL_INCLUDE=@OPENSSL_INCLUDE@
 SSL_LIB=@OPENSSL_LIB@
 
Index: configs/cdr_sqlite3_custom.conf
===================================================================
--- configs/cdr_sqlite3_custom.conf	(revision 0)
+++ configs/cdr_sqlite3_custom.conf	(revision 0)
@@ -0,0 +1,7 @@
+;
+; Mappings for custom config file
+;
+[master] ; currently, only file "master.db" is supported, with only one table at a time.
+table	=> cdr
+columns	=> calldate, clid, dcontext, channel, dstchannel, lastapp, lastdata, duration, billsec, disposition, amaflags, accountcode, uniqueid, userfield, test
+values	=> '${CDR(start)}','${CDR(clid)}','${CDR(dcontext)}','${CDR(channel)}','${CDR(dstchannel)}','${CDR(lastapp)}','${CDR(lastdata)}','${CDR(duration)}','${CDR(billsec)}','${CDR(disposition)}','${CDR(amaflags)}','${CDR(accountcode)}','${CDR(uniqueid)}','${CDR(userfield)}','${CDR(test)}'
Index: cdr/cdr_sqlite3_custom.c
===================================================================
--- cdr/cdr_sqlite3_custom.c	(revision 0)
+++ cdr/cdr_sqlite3_custom.c	(revision 0)
@@ -0,0 +1,344 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com> and others.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Custom SQLite3 CDR records.
+ *
+ * \author Adapted by Alejandro Rios <alejandro.rios@avatar.com.co> and
+ *  Russell Bryant <russell@digium.com> from 
+ *  cdr_mysql_custom by Edward Eastman <ed@dm3.co.uk>,
+ *	and cdr_sqlite by Holger Schurig <hs4233@mail.mn-solutions.de>
+ *	
+ *
+ * \arg See also \ref AstCDR
+ *
+ *
+ * \ingroup cdr_drivers
+ */
+
+/*** MODULEINFO
+	<depend>sqlite3</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sqlite3.h>
+
+#include "asterisk/paths.h"	/* use ast_config_AST_LOG_DIR */
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+
+AST_MUTEX_DEFINE_STATIC(lock);
+
+static const char config_file[] = "cdr_sqlite3_custom.conf";
+
+static const char desc[] = "Customizable SQLite3 CDR Backend";
+static const char name[] = "cdr_sqlite3_custom";
+static sqlite3 *db = NULL;
+
+static char table[80];
+static char *columns = NULL;
+
+struct values {
+	char *expression;
+	AST_LIST_ENTRY(values) list;
+};
+
+static AST_LIST_HEAD_STATIC(sql_values, values);
+
+static void free_config(int reload);
+
+
+static int load_column_config(const char *tmp)
+{
+	char *col = NULL;
+	char *cols = NULL;
+	char *escaped = NULL;
+	char coma_buf[256];
+	int coma_size = 0;
+	char column_string[1024] = "";
+
+	if (ast_strlen_zero(tmp)) {
+		ast_log(LOG_WARNING, "Column names not specified. Module not loaded.\n");
+		return -1;
+	}
+	if (!(cols = ast_strdup(tmp))) {
+		ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
+		return -1;
+	}
+	while ((col = strsep(&cols, ","))) {
+		col = ast_strip(col);
+		escaped = sqlite3_mprintf("%q", col);
+		if (!escaped) {
+			ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s.'\n", col, table);
+			return -1;
+		}
+		coma_size = snprintf(coma_buf, sizeof(coma_buf), "%s%s", strlen(column_string) ? "," : "", escaped);
+		strncat(column_string, coma_buf, coma_size);
+		sqlite3_free(escaped);
+	}
+	if (!(columns = ast_strdup(column_string))) {
+		ast_log(LOG_ERROR, "Out of memory copying columns string for table '%s.'\n", table);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int load_values_config(const char *tmp)
+{
+	char *val = NULL;
+	char *vals = NULL;
+	struct values *value = NULL;
+
+	if (ast_strlen_zero(tmp)) {
+		ast_log(LOG_WARNING, "Values not specified. Module not loaded.\n");
+		return -1;
+	}
+	if (!(vals = ast_strdup(tmp))) {
+		ast_log(LOG_ERROR, "Out of memory creating temporary buffer for value '%s'\n", tmp);
+		return -1;
+	}
+	while ((val = strsep(&vals, ","))) {
+		/* Strip the single quotes off if they are there */
+		val = ast_strip_quoted(val, "'", "'");
+		value = ast_calloc(sizeof(char), sizeof(*value) + strlen(val) + 1);
+		if (!value) {
+			ast_log(LOG_ERROR, "Out of memory creating entry for value '%s'\n", val);
+			return -1;
+		}
+		value->expression = (char *) value + sizeof(*value);
+		ast_copy_string(value->expression, val, strlen(val) + 1);
+		AST_LIST_INSERT_TAIL(&sql_values, value, list);
+	}
+
+	return 0;
+}
+
+static int load_config(int reload)
+{
+	struct ast_config *cfg;
+	struct ast_variable *mappingvar;
+	const char *tmp;
+
+	if (!(cfg = ast_config_load(config_file))) {
+		ast_log(LOG_WARNING, "Failed to %sload configuration file. %s\n", reload ? "re" : "", reload ? "" : "Module not activated.");
+		return -1;
+	}
+
+	if (reload) {
+		free_config(1);
+	}
+
+	if (!(mappingvar = ast_variable_browse(cfg, "master"))) {
+		/* Nothing configured */
+		ast_config_destroy(cfg);
+		return -1;
+	}
+
+	/* Mapping must have a table name */
+	if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "master", "table"))) {
+		ast_copy_string(table, tmp, sizeof(table));
+	} else {
+		ast_log(LOG_WARNING, "Table name not specified.  Assuming cdr.\n");
+		strcpy(table, "cdr");
+	}
+
+	/* Columns */
+	if (load_column_config(ast_variable_retrieve(cfg, "master", "columns"))) { 
+		ast_config_destroy(cfg); 
+		free_config(0); 
+		return -1; 
+	}
+
+	/* Values */
+	if (load_values_config(ast_variable_retrieve(cfg, "master", "values"))) {
+		ast_config_destroy(cfg);
+		free_config(0);
+		return -1;
+	}
+
+	//ast_verb(3, "cdr_sqlite3_custom: Logging CDR records to table '%s' in 'master.db'\n", table);
+
+	ast_config_destroy(cfg);
+
+	return 0;
+}
+
+static void free_config(int reload)
+{
+	struct values *value;
+
+	if (!reload && db) {
+		sqlite3_close(db);
+		db = NULL;
+	}
+
+	if (columns) {
+		ast_free(columns);
+		columns = NULL;
+	}
+
+	while ((value = AST_LIST_REMOVE_HEAD(&sql_values, list))) {
+		ast_free(value);
+	}
+}
+
+static int sqlite3_log(struct ast_cdr *cdr)
+{
+	int res = 0;
+	char *error = NULL;
+	char *sql = NULL;
+	int count = 0;
+
+	if (db == NULL) {
+		/* Should not have loaded, but be failsafe. */
+		return 0;
+	}
+
+	ast_mutex_lock(&lock);
+
+	{ /* Make it obvious that only sql should be used outside of this block */
+		char *escaped = NULL;
+		char subst_buf[2048];
+		char coma_buf[256];
+		int coma_size = 0;
+		struct values *value;
+		struct ast_channel dummy = { 0, };
+		char value_string[1024] = "";
+
+		dummy.cdr = ast_cdr_dup(cdr);
+		AST_LIST_TRAVERSE(&sql_values, value, list) {
+			pbx_substitute_variables_helper(&dummy, value->expression, subst_buf, sizeof(subst_buf) - 1);
+			escaped = sqlite3_mprintf("%q", subst_buf);
+			coma_size = snprintf(coma_buf, sizeof(coma_buf), "%s'%s'", strlen(value_string) ? "," : "", escaped);
+			strncat(value_string, coma_buf, coma_size);
+			sqlite3_free(escaped);
+		}
+		sql = sqlite3_mprintf("INSERT INTO %q (%s) VALUES (%s)", table, columns, &value_string);
+		ast_free(dummy.cdr);
+		ast_log(LOG_DEBUG, "About to log: %s\n", sql);
+	}
+
+	/* XXX This seems awful arbitrary... */
+	for (count = 0; count < 5; count++) {
+		res = sqlite3_exec(db, sql, NULL, NULL, &error);
+		if (res != SQLITE_BUSY && res != SQLITE_LOCKED) {
+			break;
+		}
+		usleep(200);
+	}
+
+	if (error) {
+		ast_log(LOG_ERROR, "%s. SQL: %s.\n", error, sql);
+		sqlite3_free(error);
+	}
+
+	if (sql) {
+		sqlite3_free(sql);
+	}
+
+	ast_mutex_unlock(&lock);
+
+	return res;
+}
+
+static int unload_module(void)
+{
+	ast_cdr_unregister(name);
+
+	free_config(0);
+
+	return 0;
+}
+
+static int load_module(void)
+{
+	char *error;
+	char filename[PATH_MAX];
+	int res;
+	char *sql;
+
+	if (load_config(0)) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	/* is the database there? */
+	snprintf(filename, sizeof(filename), "%s/master.db", ast_config_AST_LOG_DIR);
+	res = sqlite3_open(filename, &db);
+	if (res != SQLITE_OK) {
+		ast_log(LOG_ERROR, "Could not open database %s.\n", filename);
+		free_config(0);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	/* is the table there? */
+	sql = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table);
+	res = sqlite3_exec(db, sql, NULL, NULL, NULL);
+	sqlite3_free(sql);
+	if (res != SQLITE_OK) {
+		/* We don't use %q for the column list here since we already escaped when building it */
+		sql = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY, %s)", table, columns);
+		res = sqlite3_exec(db, sql, NULL, NULL, &error);
+		sqlite3_free(sql);
+		if (res != SQLITE_OK) {
+			ast_log(LOG_WARNING, "Unable to create table '%s': %s.\n", table, error);
+			sqlite3_free(error);
+			free_config(0);
+			return AST_MODULE_LOAD_DECLINE;
+		}
+	}
+
+	res = ast_cdr_register(name, desc, sqlite3_log);
+	if (res) {
+		ast_log(LOG_ERROR, "Unable to register custom SQLite3 CDR handling\n");
+		free_config(0);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload(void)
+{
+	int res = 0;
+
+	ast_mutex_lock(&lock);
+	res = load_config(1);
+	ast_mutex_unlock(&lock);
+
+	return res;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SQLite3 Custom CDR Module",
+	.load = load_module,
+	.unload = unload_module,
+	.reload = reload,
+);
+
