Initial commit

Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/extensions/app_acct/CMakeLists.txt b/extensions/app_acct/CMakeLists.txt
new file mode 100644
index 0000000..433ca5d
--- /dev/null
+++ b/extensions/app_acct/CMakeLists.txt
@@ -0,0 +1,38 @@
+# The app_acct extension
+PROJECT("Simple Accounting server" C)
+
+########################
+# Search for libpg (postgresql package)
+FIND_PACKAGE(PostgreSQL REQUIRED)
+INCLUDE_DIRECTORIES(${POSTGRESQL_INCLUDE_DIR})
+
+########################
+# Parser files
+BISON_FILE(acct_conf.y)
+FLEX_FILE(acct_conf.l)
+SET_SOURCE_FILES_PROPERTIES(lex.acct_conf.c acct_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+
+# List of source files
+SET( APP_ACCT_SRC
+	app_acct.h
+	app_acct.c
+	acct_db.c
+	acct_records.c
+)
+SET( APP_ACCT_SRC_GEN
+	lex.acct_conf.c
+	acct_conf.tab.c
+	acct_conf.tab.h
+)
+
+# Compile as a module
+FD_ADD_EXTENSION(app_acct ${APP_ACCT_SRC} ${APP_ACCT_SRC_GEN})
+TARGET_LINK_LIBRARIES(app_acct ${POSTGRESQL_LIBRARIES})
+
+
+####
+## INSTALL section ##
+
+INSTALL(TARGETS app_acct
+	LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX}
+	COMPONENT freeDiameter-accounting-server)
diff --git a/extensions/app_acct/acct_conf.l b/extensions/app_acct/acct_conf.l
new file mode 100644
index 0000000..c25de1a
--- /dev/null
+++ b/extensions/app_acct/acct_conf.l
@@ -0,0 +1,143 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Lex extension's configuration parser.
+ *
+ */
+
+%{
+#include "app_acct.h"
+#include "acct_conf.tab.h"
+
+/* Update the column information */
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+}
+
+/* Avoid warning with newer flex */
+#define YY_NO_INPUT
+
+%}
+
+%option bison-bridge bison-locations
+%option noyywrap
+%option nounput
+
+/* Quoted string. Multilines do not match. */
+qstring		\"[^\"\n]*\"
+
+%%
+
+	/* Update the line count */
+\n			{
+				yylloc->first_line++; 
+				yylloc->last_line++; 
+				yylloc->last_column=0; 
+			}
+	 
+	/* Eat all spaces but not new lines */
+([[:space:]]{-}[\n])+	;
+	/* Eat all comments */
+#.*$			;
+
+	/* Recognize any integer */
+[-]?[[:digit:]]+		{
+				/* Convert this to an integer value */
+				int ret=0;
+				ret = sscanf(yytext, "%i", &yylval->integer);
+				if (ret != 1) {
+					/* No matching: an error occurred */
+					fd_log_debug("Unable to convert the value '%s' to a valid number: %s", yytext, strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+					/* Maybe we could REJECT instead of failing here? */
+				}
+				return INTEGER;
+			}
+
+	/* Recognize quoted strings -- we do not support escaped \" in the string currently. */
+{qstring}		{
+				/* Match a quoted string. Let's be very permissive. */
+				yylval->string = strdup(yytext+1);
+				if (!yylval->string) {
+					fd_log_debug("Unable to copy the string '%s': %s", yytext, strerror(errno));
+					TRACE_DEBUG(INFO, "strdup failed");
+					return LEX_ERROR; /* trig an error in yacc parser */
+				}
+				yylval->string[strlen(yytext) - 2] = '\0';
+				return QSTRING;
+			}
+			
+	/* Recognize the tokens */	
+(?i:"ConnInfo")		{
+				return CONNINFO;
+			}
+
+(?i:"Table")		{
+				return TABLE;
+			}
+
+(?i:"Timestamp_field")	{
+				return TSFIELD;
+			}
+
+(?i:"Server_name_field") {
+				return SRVNFIELD;
+			}
+
+(?i:"field")		{
+				return FIELD;
+			}
+
+(?i:"required")		{
+				return REQUIRED;
+			}
+
+(?i:"multi")		{
+				return MULTI;
+			}
+
+
+			
+	/* Valid single characters for yyparse */
+[=;{}]			{ return yytext[0]; }
+
+	/* Unrecognized sequence, if it did not match any previous pattern */
+[^[:space:]"*=>;\n]+	{ 
+				fd_log_debug("Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext);
+			 	return LEX_ERROR; 
+			}
+
+%%
diff --git a/extensions/app_acct/acct_conf.y b/extensions/app_acct/acct_conf.y
new file mode 100644
index 0000000..f79f1fa
--- /dev/null
+++ b/extensions/app_acct/acct_conf.y
@@ -0,0 +1,336 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2015, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Yacc extension's configuration parser.
+ * See doc/app_acct.conf.sample for configuration file format
+ */
+
+/* For development only : */
+%debug 
+%error-verbose
+
+/* The parser receives the configuration file filename as parameter */
+%parse-param {char * conffile}
+
+/* Keep track of location */
+%locations 
+%pure-parser
+
+%{
+#include "app_acct.h"
+#include "acct_conf.tab.h"
+
+#include <string.h>
+#include <errno.h>
+
+/* Forward declaration */
+int yyparse(char * conffile);
+
+/* The Lex parser prototype */
+int acct_conflex(YYSTYPE *lvalp, YYLTYPE *llocp);
+
+/* the global configuration */
+struct acct_conf * acct_config = NULL;
+
+/* Initialize the blank configuration structure */
+int acct_conf_init(void)
+{
+	TRACE_ENTRY();
+	
+	CHECK_MALLOC( acct_config = malloc(sizeof(struct acct_conf)) );
+	memset(acct_config, 0, sizeof(struct acct_conf) );
+	fd_list_init(&acct_config->avps, NULL);
+	
+	return 0;
+}
+
+/* Validate and eventually display the content of the configuration file for debug */
+int acct_conf_check(char * conffile)
+{
+	CHECK_PARAMS(acct_config);
+	
+	if ( ! acct_config->conninfo) {
+		fd_log_debug("[app_acct] ERROR: 'ConnInfo' is missing in file '%s'."
+			" You can specify 'ConnInfo=\"\";' to use default parameters.", conffile);
+		return EINVAL;
+	}
+	if ( ! acct_config->tablename) {
+		fd_log_debug("[app_acct] ERROR: 'Table' value is missing in file '%s'.", conffile);
+		return EINVAL;
+	}
+
+	if (!TRACE_BOOL(FULL))
+		return 0;
+	
+	struct fd_list * li;
+		
+	fd_log_debug("[app_acct] Configuration dump:");
+	fd_log_debug(" Database:");
+	fd_log_debug("   ConnInfo ...... : '%s'", acct_config->conninfo ?: "<null>");
+	fd_log_debug("   Table name .... : '%s'", acct_config->tablename ?: "<null>");
+	fd_log_debug("   Timestamp field : '%s'", acct_config->tsfield ?: "<null>");
+	fd_log_debug("   Server name fld : '%s'", acct_config->srvnfield ?: "<null>");
+	fd_log_debug(" AVPs that will be saved to the database:");
+	for (li = acct_config->avps.next; li != &acct_config->avps; li = li->next) {
+		struct acct_conf_avp * a = (struct acct_conf_avp *)li;
+		fd_log_debug("   %-*s AVP%s saved in ", 30, a->avpname, a->required ? " [required]":"" );
+		if (a->multi) {
+			fd_log_debug("fields '%s[1..%d]' ", a->field?:a->avpname, a->multi);
+		} else {
+			fd_log_debug("field '%s' ", a->field?:a->avpname);
+		}
+		fd_log_debug("as ::%s", diam2db_types_mapping[a->avptype]);
+	}
+	fd_log_debug("[app_acct] Complete.");
+	return 0;
+}
+
+void acct_conf_free(void)
+{
+	TRACE_ENTRY();
+	
+	if (!acct_config)
+		return;
+		
+	/* Destroy the list */
+	while (!FD_IS_LIST_EMPTY(&acct_config->avps)) {
+		struct acct_conf_avp * a = (struct acct_conf_avp *)(acct_config->avps.next);
+		fd_list_unlink(&a->chain);
+		free(a->avpname);
+		free(a->field);
+		free(a);
+	}
+	
+	/* destroy other data */
+	free(acct_config->conninfo);
+	free(acct_config->tablename);
+	free(acct_config->tsfield);
+	free(acct_config->srvnfield);
+	
+	/* Done */
+	free(acct_config);
+	acct_config = NULL;
+}
+
+/* Parse the configuration file */
+int acct_conf_parse(char * conffile)
+{
+	extern FILE * acct_confin;
+	int ret;
+	
+	TRACE_ENTRY("%p", conffile);
+	
+	TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile);
+	
+	acct_confin = fopen(conffile, "r");
+	if (acct_confin == NULL) {
+		ret = errno;
+		fd_log_debug("Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret));
+		return ret;
+	}
+
+	ret = yyparse(conffile);
+
+	fclose(acct_confin);
+
+	if (ret != 0) {
+		TRACE_DEBUG (INFO, "Unable to parse the configuration file.");
+		return EINVAL;
+	}
+	
+	return 0;
+}
+
+/* Function to report the errors */
+void yyerror (YYLTYPE *ploc, char * conffile, char const *s)
+{
+	LOG_E( "Error in configuration parsing");
+	
+	if (ploc->first_line != ploc->last_line)
+		LOG_E("%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
+	else if (ploc->first_column != ploc->last_column)
+		LOG_E("%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
+	else
+		LOG_E("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s);
+}
+
+static struct acct_conf_avp avpdata;
+
+%}
+
+/* Values returned by lex for token */
+%union {
+	char 		*string;	/* The string is allocated by strdup in lex.*/
+	int		 integer;	/* Store integer values */
+}
+
+/* In case of error in the lexical analysis */
+%token 		LEX_ERROR
+
+/* Keywords */
+%token 		FIELD
+%token 		REQUIRED
+%token 		MULTI
+%token 		CONNINFO
+%token 		TABLE
+%token 		TSFIELD
+%token 		SRVNFIELD
+
+/* Tokens and types */
+/* A (de)quoted string (malloc'd in lex parser; it must be freed after use) */
+%token <string>	QSTRING
+
+/* An integer value */
+%token <integer> INTEGER
+
+
+
+/* -------------------------------------- */
+%%
+
+	/* The grammar definition */
+conffile:		/* empty grammar is OK for the parser (will be validated afterwards) */
+			| conffile avpline
+			| conffile conninfoline
+			| conffile tableline
+			| conffile tsfieldline
+			| conffile srvnfieldline
+			| conffile errors
+			{
+				yyerror(&yylloc, conffile, "An error occurred while parsing the configuration file.");
+				return EINVAL;
+			}
+			;
+
+			/* Catch lexical and syntax errors */
+errors:			LEX_ERROR
+			| error
+			;
+
+	/* The tokens */
+avpline:		{
+				memset(&avpdata, 0, sizeof(struct acct_conf_avp));
+			}
+			QSTRING avpcontents ';'
+			{
+				struct acct_conf_avp *new;
+				struct dict_object * dict;
+				struct dict_avp_data dictdata;
+				
+				/* Validate the avp name first */
+				CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, $2, &dict, ENOENT),
+					{ yyerror (&yylloc, conffile, "AVP definition not found in the dictionary. Was the appropriate dict_*.fdx extension loaded?"); YYERROR; } );
+				CHECK_FCT( fd_dict_getval( dict, &dictdata ));
+				
+				/* Create a new entry */
+				CHECK_MALLOC_DO( new = malloc(sizeof(struct acct_conf_avp)),
+					{ yyerror (&yylloc, conffile, "Out of memory"); YYERROR; } );
+				
+				/* Retrieve all the data from avpcontents parsing (field, required, multi) */	
+				memcpy(new, &avpdata, sizeof(struct acct_conf_avp));
+				
+				/* Initialize the other data */
+				fd_list_init(&new->chain, NULL);
+				new->avpname = $2;
+				new->avpobj = dict;
+				new->avptype = dictdata.avp_basetype;
+			
+				/* Add this new entry at the end of the list */
+				fd_list_insert_before( &acct_config->avps, &new->chain );
+			}
+			;
+			
+avpcontents:		/* Empty content is fine */
+			| '=' '{' avpflagline '}'
+			;
+			
+avpflagline:		/* Empty flags is also fine */
+			| avpflagline FIELD '=' QSTRING ';'
+			{
+				if (avpdata.field) {
+					yyerror (&yylloc, conffile, "Duplicate entry");
+					YYERROR;
+				}
+				avpdata.field = $4;
+			}
+			| avpflagline REQUIRED ';'
+			{
+				avpdata.required = 1;
+			}
+			| avpflagline MULTI '=' INTEGER ';'
+			{
+				avpdata.multi = (unsigned) $4;
+			}
+			;
+	
+conninfoline:		CONNINFO '=' QSTRING ';'
+			{
+				if (acct_config->conninfo) {
+					yyerror (&yylloc, conffile, "Duplicate entry");
+					YYERROR;
+				}
+				acct_config->conninfo = $3;
+			}
+			;
+
+tableline:		TABLE '=' QSTRING ';'
+			{
+				if (acct_config->tablename) {
+					yyerror (&yylloc, conffile, "Duplicate entry");
+					YYERROR;
+				}
+				acct_config->tablename = $3;
+			}
+			;
+
+tsfieldline:		TSFIELD '=' QSTRING ';'
+			{
+				if (acct_config->tsfield) {
+					yyerror (&yylloc, conffile, "Duplicate entry");
+					YYERROR;
+				}
+				acct_config->tsfield = $3;
+			}
+			;
+
+srvnfieldline:		SRVNFIELD '=' QSTRING ';'
+			{
+				if (acct_config->srvnfield) {
+					yyerror (&yylloc, conffile, "Duplicate entry");
+					YYERROR;
+				}
+				acct_config->srvnfield = $3;
+			}
+			;
diff --git a/extensions/app_acct/acct_db.c b/extensions/app_acct/acct_db.c
new file mode 100644
index 0000000..a444c94
--- /dev/null
+++ b/extensions/app_acct/acct_db.c
@@ -0,0 +1,341 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Database interface module */
+
+/* There is one connection to the db per thread. 
+The connection is stored in the pthread_key_t variable */
+
+
+#include "app_acct.h"
+#include <libpq-fe.h>
+
+const char * diam2db_types_mapping[AVP_TYPE_MAX + 1] = {
+	"" 		/* AVP_TYPE_GROUPED */,
+	"bytea" 	/* AVP_TYPE_OCTETSTRING */,
+	"integer" 	/* AVP_TYPE_INTEGER32 */,
+	"bigint" 	/* AVP_TYPE_INTEGER64 */,
+	"integer" 	/* AVP_TYPE_UNSIGNED32 + cast */,
+	"bigint" 	/* AVP_TYPE_UNSIGNED64 + cast */,
+	"real" 		/* AVP_TYPE_FLOAT32 */,
+	"double precision" /* AVP_TYPE_FLOAT64 */
+};
+
+static const char * stmt = "acct_db_stmt";
+#ifndef TEST_DEBUG
+static 
+#endif /* TEST_DEBUG */
+pthread_key_t connk;
+static char * sql = NULL;   /* The buffer that will contain the SQL query */
+static int nbrecords = 0;
+
+
+/* Initialize the database context: connection to the DB, prepared statement to insert new records */
+int acct_db_init(void)
+{
+	struct acct_record_list emptyrecords;
+	struct fd_list * li;
+	size_t sql_allocd = 0; /* The malloc'd size of the buffer */
+	size_t sql_offset = 0; /* The actual data already written in this buffer */
+	int idx = 0;
+	PGresult * res;
+	PGconn *conn;
+	#define REALLOC_SIZE	1024	/* We extend the buffer by this amount */
+	
+	TRACE_ENTRY();
+	CHECK_PARAMS( acct_config && acct_config->conninfo && acct_config->tablename ); 
+	
+	CHECK_PARAMS_DO( PQisthreadsafe() == 1, {
+		fd_log_debug("You PostGreSQL installation is not thread-safe!");
+		return EINVAL;
+	} );			
+	
+	/* Use the information from acct_config to create the connection and prepare the query */
+	conn = PQconnectdb(acct_config->conninfo);
+	
+	/* Check to see that the backend connection was successfully made */
+	if (PQstatus(conn) != CONNECTION_OK) {
+		fd_log_debug("Connection to database failed: %s", PQerrorMessage(conn));
+		acct_db_free();
+		return EINVAL;
+	}
+	if (PQprotocolVersion(conn) < 3) {
+		fd_log_debug("Database protocol version is too old, version 3 is required for prepared statements.");
+		acct_db_free();
+		return EINVAL;
+	}
+	
+	TRACE_DEBUG(FULL, "Connection to database successful, server version %d.", PQserverVersion(conn));
+	
+	/* Now, prepare the request object */
+	
+	/* First, we build the list of AVP we will insert in the database */
+	CHECK_FCT( acct_rec_prepare(&emptyrecords) );
+	
+	/* Now, prepare the text of the request */
+	CHECK_MALLOC(sql = malloc(REALLOC_SIZE));
+	sql_allocd = REALLOC_SIZE;
+	
+	/* This macro hides the details of extending the buffer on each sprintf... */
+	#define ADD_EXTEND(args...) {									\
+		size_t p;										\
+		int loop = 0;										\
+		do {											\
+			p = snprintf(sql + sql_offset, sql_allocd - sql_offset, ##args);		\
+			if (p >= sql_allocd - sql_offset) {						\
+				/* Too short, extend the buffer and start again */			\
+				CHECK_MALLOC( sql = realloc(sql, sql_allocd + REALLOC_SIZE) );		\
+				sql_allocd += REALLOC_SIZE;						\
+				loop++;									\
+				ASSERT(loop < 100); /* detect infinite loops */				\
+				continue;								\
+			}										\
+			sql_offset += p;								\
+			break;										\
+		} while (1);										\
+	}
+	
+	/* This macro allows to add a value in the SQL buffer while escaping in properly */
+	#define ADD_ESCAPE(str) {									\
+		char * __s = (char *)str;								\
+		/* Check we have at least twice the size available +1 */				\
+		size_t p = strlen(__s);									\
+													\
+		while (sql_allocd - sql_offset < 2 * p + 1) {						\
+			/* Too short, extend the buffer */						\
+			CHECK_MALLOC( sql = realloc(sql, sql_allocd + REALLOC_SIZE) );			\
+			sql_allocd += REALLOC_SIZE;							\
+		}											\
+													\
+		/* Now add the escaped string */							\
+		p = PQescapeStringConn(conn, sql+sql_offset, __s, p, NULL);				\
+		sql_offset += p;									\
+	}
+	
+	/* INSERT INTO table (tsfield, field1, field2, ...) VALUES (now, $1::bytea, $2::integer, ...) */
+	ADD_EXTEND("INSERT INTO %s (", acct_config->tablename);
+	
+	if (acct_config->tsfield) {
+		ADD_EXTEND("\"");
+		ADD_ESCAPE(acct_config->tsfield);
+		ADD_EXTEND("\", ");
+	}
+	
+	if (acct_config->srvnfield) {
+		ADD_EXTEND("\"");
+		ADD_ESCAPE(acct_config->srvnfield);
+		ADD_EXTEND("\", ");
+	}
+	
+	for (li = emptyrecords.all.next; li != &emptyrecords.all; li = li->next) {
+		struct acct_record_item * i = (struct acct_record_item *)(li->o);
+		ADD_EXTEND("\"");
+		ADD_ESCAPE(i->param->field?:i->param->avpname);
+		if (i->index) {
+			ADD_EXTEND("%d", i->index);
+		}
+		if (li->next != &emptyrecords.all) {
+			ADD_EXTEND("\", ");
+		}
+	}
+	
+	ADD_EXTEND("\") VALUES (");
+	
+	if (acct_config->tsfield) {
+		++idx;
+		ADD_EXTEND("$%d, ", idx);
+	}
+	if (acct_config->srvnfield) {
+		ADD_EXTEND("'");
+		ADD_ESCAPE(fd_g_config->cnf_diamid);
+		ADD_EXTEND("', ");
+	}
+	
+	for (li = emptyrecords.all.next; li != &emptyrecords.all; li = li->next) {
+		struct acct_record_item * i = (struct acct_record_item *)(li->o);
+		++idx;
+		ADD_EXTEND("$%d::%s", idx, diam2db_types_mapping[i->param->avptype]);
+		
+		if (li->next != &emptyrecords.all) {
+			ADD_EXTEND(", ");
+		}
+	}
+	
+	ADD_EXTEND(");");
+	
+	TRACE_DEBUG(FULL, "Preparing the following SQL statement: '%s'", sql);
+	res = PQprepare(conn, stmt, sql, emptyrecords.nball, NULL);
+	if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+		TRACE_DEBUG(INFO, "Preparing statement '%s' failed: %s",
+			sql, PQerrorMessage(conn));
+		PQclear(res);
+		return EINVAL;
+        }
+	PQclear(res);
+	nbrecords = emptyrecords.nball;
+	
+	acct_rec_empty(&emptyrecords);
+	
+	CHECK_POSIX( pthread_key_create(&connk, (void (*)(void*))PQfinish) );
+	CHECK_POSIX( pthread_setspecific(connk, conn) );
+	
+	/* Ok, ready */
+	return 0;
+}
+
+/* Terminate the connection to the DB */
+void acct_db_free(void)
+{	
+	CHECK_POSIX_DO(pthread_key_delete(connk) , );
+	free(sql);
+}
+
+/* When a new message has been received, insert the content of the parsed mapping into the DB (using prepared statement) */
+int acct_db_insert(struct acct_record_list * records)
+{
+	char 	**val;
+	int	 *val_len;
+	int 	 *val_isbin;
+	int	  idx = 0;
+	int	  size = 0;
+	PGresult *res;
+	struct fd_list *li;
+	PGconn *conn;
+	int new = 0;
+	
+	TRACE_ENTRY("%p", records);
+	CHECK_PARAMS( records );
+	
+	conn = pthread_getspecific(connk);
+	if (!conn) {
+		conn = PQconnectdb(acct_config->conninfo);
+		CHECK_POSIX( pthread_setspecific(connk, conn) );
+		
+		new = 1;
+	}
+	
+	/* First, check if the connection with the DB has not staled, and eventually try to fix it */
+	if (PQstatus(conn) != CONNECTION_OK) {
+		/* Attempt a reset */
+		PQreset(conn);
+		if (PQstatus(conn) != CONNECTION_OK) {
+			TRACE_DEBUG(INFO, "Lost connection to the database server, and attempt to reestablish it failed");
+			TODO("Terminate the freeDiameter instance completely?");
+			return ENOTCONN;
+		}
+	}
+	
+	if (new) {
+		/* Create the prepared statement for this ocnnection, it is not shared */
+		res = PQprepare(conn, stmt, sql, nbrecords, NULL);
+		if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+			TRACE_DEBUG(INFO, "Preparing statement '%s' failed: %s",
+				sql, PQerrorMessage(conn));
+			PQclear(res);
+			return EINVAL;
+        	}
+		PQclear(res);
+	}
+	
+	size = 	acct_config->tsfield ? records->nball + 1 : records->nball;
+	
+	/* Alloc the arrays of parameters */
+	CHECK_MALLOC( val       = calloc(size, sizeof(const char *)) );
+	CHECK_MALLOC( val_len   = calloc(size, sizeof(const int)) );
+	CHECK_MALLOC( val_isbin = calloc(size, sizeof(const int)) );
+	
+	if (acct_config->tsfield) {
+		val[idx] = "now";
+		val_len[idx] = 3;
+		val_isbin[idx] = 0;
+		idx++;
+	}
+	
+	/* Now write all the map'd records in these arrays */
+	for (li = records->all.next; li != &records->all; li = li->next) {
+		struct acct_record_item * r = (struct acct_record_item *)(li->o);
+		if (r->value) {
+			val_isbin[idx] = 1; /* We always pass binary parameters */
+			switch (r->param->avptype) {
+				case AVP_TYPE_OCTETSTRING:
+					val[idx] = (void *)(r->value->os.data);
+					val_len[idx] = r->value->os.len;
+					break;
+					
+				case AVP_TYPE_INTEGER32:
+				case AVP_TYPE_UNSIGNED32:
+				case AVP_TYPE_FLOAT32:
+					r->scalar.v32 = htonl(r->value->u32);
+					val[idx] = &r->scalar.c;
+					val_len[idx] = sizeof(uint32_t);
+					break;
+					
+				case AVP_TYPE_INTEGER64:
+				case AVP_TYPE_UNSIGNED64:
+				case AVP_TYPE_FLOAT64:
+					r->scalar.v64 = htonll(r->value->u64);
+					val[idx] = &r->scalar.c;
+					val_len[idx] = sizeof(uint64_t);
+					break;
+				
+				default:
+					ASSERT(0); /* detect bugs */
+			}
+		}
+		
+		idx++;
+	}
+	
+	/* OK, now execute the SQL statement */
+	res = PQexecPrepared(conn, stmt, size, (const char * const *)val, val_len, val_isbin, 1 /* We actually don't care here */);
+	
+	/* Done with the parameters */
+	free(val);
+	free(val_len);
+	free(val_isbin);
+	
+	/* Now check the result code */
+	if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+		TRACE_DEBUG(INFO, "An error occurred while INSERTing in the database: %s", PQerrorMessage(conn));
+		PQclear(res);
+		return EINVAL; /* It was probably a mistake in configuration file... */
+        }
+	PQclear(res);
+	
+	/* Ok, we are done */
+	return 0;
+}
+
+
diff --git a/extensions/app_acct/acct_records.c b/extensions/app_acct/acct_records.c
new file mode 100644
index 0000000..4f1b340
--- /dev/null
+++ b/extensions/app_acct/acct_records.c
@@ -0,0 +1,161 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Functions to create a list of AVPs according to the configuration file */
+
+#include "app_acct.h"
+
+/* Prepare a list of acct_record entries from the configuration, without mapping any value at this time */
+int acct_rec_prepare(struct acct_record_list * records)
+{
+	struct fd_list * li;
+	TRACE_ENTRY("%p", records);
+	CHECK_PARAMS( records && acct_config );
+	
+	/* Prepare the records structure */
+	memset(records, 0, sizeof(struct acct_record_list));
+	fd_list_init(&records->all, records);
+	fd_list_init(&records->unmaped, records);
+	
+	/* for each entry in the configuration */
+	for (li = acct_config->avps.next; li != &acct_config->avps; li = li->next) {
+		struct acct_conf_avp * a = (struct acct_conf_avp *)li;
+		struct acct_record_item * new;
+		int i = a->multi ? 1 : 0;
+		/* Create as many records as the 'multi' parameter requires */
+		do {
+			CHECK_MALLOC( new = malloc(sizeof(struct acct_record_item)) );
+			memset(new, 0, sizeof(struct acct_record_item));
+			fd_list_init(&new->chain, new);
+			fd_list_init(&new->unmapd, new);
+			new->param = a;
+			new->index = i;
+			fd_list_insert_before(&records->all, &new->chain);
+			fd_list_insert_before(&records->unmaped, &new->unmapd);
+			records->nball++;
+			records->nbunmap++;
+			i++;
+		} while (i <= a->multi);
+	}
+	
+	return 0;
+}
+
+/* Find the AVPs from configuration inside a received message */
+int acct_rec_map(struct acct_record_list * records, struct msg * msg)
+{
+	struct avp * avp;
+	
+	TRACE_ENTRY("%p %p", records, msg);
+	
+	/* For each AVP in the message, search if we have a corresponding unmap'd record */
+	CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
+	while (avp) {
+		struct fd_list * li;
+		struct dict_object * model;
+		
+		CHECK_FCT( fd_msg_model(avp, &model) );
+		if (model != NULL) {	/* we ignore the AVPs we don't recognize */
+		
+			/* Search this model in the list */
+			for (li = records->unmaped.next; li != &records->unmaped; li = li->next) {
+				struct acct_record_item * r = (struct acct_record_item *)(li->o);
+				if (r->param->avpobj == model) {
+					/* It matches: save the AVP value and unlink this record from the unmap'd list */
+					struct avp_hdr * h;
+					CHECK_FCT( fd_msg_avp_hdr( avp, &h ) );
+					r->value = h->avp_value;
+					fd_list_unlink(&r->unmapd);
+					records->nbunmap -= 1;
+					break;
+				}
+			}
+
+			/* Continue only while there are some AVPs to map */
+			if (FD_IS_LIST_EMPTY(&records->unmaped))
+				break;
+		}
+		
+		/* Go to next AVP in the message */
+		CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
+	}
+	
+	/* Done */
+	return 0;
+}
+
+/* Check that a mapped list is not empty and no required AVP is missing. Free the record list in case of error */
+int acct_rec_validate(struct acct_record_list * records)
+{
+	struct fd_list * li;
+	TRACE_ENTRY("%p", records);
+	CHECK_PARAMS( records );
+	
+	/* Check at least one AVP was mapped */
+	if (records->nball == records->nbunmap) {
+		fd_log_debug("The received ACR does not contain any AVP from the configuration file."
+				" This is an invalid situation. Please fix your configuration file."
+				" One way to ensure this does not happen is to include Session-Id in the database.");
+		acct_rec_empty(records);
+		return EINVAL;
+	}
+	
+	/* Now, check there is no required AVP unmap'd */
+	for (li = records->unmaped.next; li != &records->unmaped; li = li->next) {
+		struct acct_record_item * r = (struct acct_record_item *)(li->o);
+		if (r->param->required && (r->index <= 1)) {
+			fd_log_debug("The received ACR does not contain the required AVP '%s'.", r->param->avpname);
+			acct_rec_empty(records);
+			return EINVAL;
+		}
+	}
+	
+	/* The record list is OK */
+	return 0;
+}
+
+/* Free all the items in an acct_record_list returned by acct_rec_prepare */
+void acct_rec_empty(struct acct_record_list * records)
+{
+	TRACE_ENTRY("%p", records);
+	CHECK_PARAMS_DO( records, return );
+	
+	while (!FD_IS_LIST_EMPTY(&records->all)) {
+		struct acct_record_item * r = (struct acct_record_item *)(records->all.next);
+		fd_list_unlink( &r->chain );
+		fd_list_unlink( &r->unmapd );
+		free(r);
+	}
+}
diff --git a/extensions/app_acct/app_acct.c b/extensions/app_acct/app_acct.c
new file mode 100644
index 0000000..9465b75
--- /dev/null
+++ b/extensions/app_acct/app_acct.c
@@ -0,0 +1,155 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* The simple Accounting server for freeDiameter */
+
+#include "app_acct.h"
+
+/* Mandatory AVPs for the Accounting-Answer (any value in adding all the other AVPs?) */
+static struct {
+	struct dict_object * Accounting_Record_Number;
+	struct dict_object * Accounting_Record_Type;
+} acct_dict;
+
+
+/* Callback for incoming Base Accounting Accounting-Request messages */
+static int acct_cb( struct msg ** msg, struct avp * avp, struct session * sess, void * opaque, enum disp_action * act)
+{
+	struct msg * m;
+	struct avp * a = NULL;
+	struct avp_hdr * art=NULL, *arn=NULL; /* We keep a pointer on the Accounting-Record-{Type, Number} AVPs from the query */
+	struct acct_record_list rl;
+	
+	TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act);
+	if (msg == NULL)
+		return EINVAL;
+	
+	m = *msg;
+	
+	/* Prepare a new record list */
+	CHECK_FCT( acct_rec_prepare( &rl ) );
+	
+	/* Maps the AVPs from the query with this record list */
+	CHECK_FCT( acct_rec_map( &rl, m ) );
+	
+	/* Check that at least one AVP was mapped */
+	CHECK_FCT( acct_rec_validate( &rl ) );
+	
+	/* Now, save these mapped AVPs in the database */
+	CHECK_FCT( acct_db_insert( &rl ) );
+	
+	acct_rec_empty( &rl );
+	
+	/* OK, we can send a positive reply now */
+	
+	/* Get Accounting-Record-{Number,Type} values */
+	CHECK_FCT( fd_msg_search_avp ( m, acct_dict.Accounting_Record_Type, &a) );
+	if (a) {
+		CHECK_FCT( fd_msg_avp_hdr( a, &art )  );
+	}
+	CHECK_FCT( fd_msg_search_avp ( m, acct_dict.Accounting_Record_Number, &a) );
+	if (a) {
+		CHECK_FCT( fd_msg_avp_hdr( a, &arn )  );
+	}
+	
+	/* Create the answer message */
+	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
+	m = *msg;
+
+	/* Set the Origin-Host, Origin-Realm, Result-Code AVPs */
+	CHECK_FCT( fd_msg_rescode_set( m, "DIAMETER_SUCCESS", NULL, NULL, 1 ) );
+	
+	/* Add the mandatory AVPs in the ACA */
+	if (art) {
+		CHECK_FCT( fd_msg_avp_new ( acct_dict.Accounting_Record_Type, 0, &a ) );
+		CHECK_FCT( fd_msg_avp_setvalue( a, art->avp_value ) );
+		CHECK_FCT( fd_msg_avp_add( m, MSG_BRW_LAST_CHILD, a ) );
+	}
+	if (arn) {
+		CHECK_FCT( fd_msg_avp_new ( acct_dict.Accounting_Record_Number, 0, &a ) );
+		CHECK_FCT( fd_msg_avp_setvalue( a, arn->avp_value ) );
+		CHECK_FCT( fd_msg_avp_add( m, MSG_BRW_LAST_CHILD, a ) );
+	}
+	
+	/* Send the answer */
+	*act = DISP_ACT_SEND;
+	return 0;
+}
+	
+
+/* entry point */
+static int acct_entry(char * conffile)
+{
+	struct disp_when data;
+	
+	TRACE_ENTRY("%p", conffile);
+	
+#ifndef TEST_DEBUG /* We do this differently in the test scenario */
+	/* Initialize the configuration and parse the file */
+	CHECK_FCT( acct_conf_init() );
+	CHECK_FCT( acct_conf_parse(conffile) );
+	CHECK_FCT( acct_conf_check(conffile) );
+#endif /* TEST_DEBUG */
+	
+	/* Now initialize the database module */
+	CHECK_FCT( acct_db_init() );
+	
+	/* Search the AVPs we will need in this file */
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &acct_dict.Accounting_Record_Number, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &acct_dict.Accounting_Record_Type, ENOENT) );
+	
+	/* Register the dispatch callbacks */
+	memset(&data, 0, sizeof(data));
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Base Accounting", &data.app, ENOENT) );
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &data.command, ENOENT) );
+	CHECK_FCT( fd_disp_register( acct_cb, DISP_HOW_CC, &data, NULL, NULL ) );
+	
+	/* Advertise the support for the Diameter Base Accounting application in the peer */
+	CHECK_FCT( fd_disp_app_support ( data.app, NULL, 0, 1 ) );
+	
+	return 0;
+}
+
+/* Unload */
+void fd_ext_fini(void)
+{
+	/* Close the db connection */
+	acct_db_free();
+	
+	/* Destroy the configuration */
+	acct_conf_free();
+}
+
+EXTENSION_ENTRY("app_acct", acct_entry);
diff --git a/extensions/app_acct/app_acct.h b/extensions/app_acct/app_acct.h
new file mode 100644
index 0000000..a98a335
--- /dev/null
+++ b/extensions/app_acct/app_acct.h
@@ -0,0 +1,121 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Header file for the app_acct extension. 
+ *
+ *  This extension is a simple Diameter Accounting server.
+ *
+ * It receives the Diameter Accounting-Request message, and sends its content in a 
+ * "buffer" postgreSQL database (see configuration sample file app_acct.conf.sample).
+ *
+ * The intent is that another application will then pick the records from this "buffer" database
+ * and process it accordingly to the requirements of the end application.
+ */
+ 
+#include <freeDiameter/extension.h>
+
+/* The structure corresponding to an AVP entry in the configuration file */
+struct acct_conf_avp {
+	/* Chain */
+	struct fd_list	 	 chain;		/* link in the global list */
+	
+	/* Raw information from the configuration file */
+	char 			*avpname;	/* the name of the AVP, as appear in the configuration file */
+	char			*field;		/* the field name in the database, or NULL if it is the same as avpname */
+	int			 required;	/* set to true if this AVP has to be in the message */
+	unsigned 		 multi;		/* the number of occurrences of this AVP we convert, or 0 if it was not specified */
+	
+	/* Parsed information */
+	struct dict_object 	*avpobj;	/* the dictionary object corresponding to this AVP */
+	enum dict_avp_basetype	 avptype;	/* this info is extracted from avpobj. GROUPED avps are not allowed yet */
+};
+
+/* This is described only inside acct_db.c */
+struct acct_db;
+
+/* The complete configuration */
+struct acct_conf {
+	/* AVPs */
+	struct fd_list	 avps;		/* the list of acct_conf_avp entries */
+	
+	/* Raw information */
+	char		*conninfo;	/* the connection string to the database, that is passed as is to the database library */
+	char 		*tablename;	/* the name of the table we are working with */
+	char 		*tsfield;	/* the name of the timestamp field, or NULL if not required */
+	char 		*srvnfield;	/* the name of the server name field, or NULL if not required */
+};
+
+/* A successfully parsed Accounting-Request produces a list of these: */
+struct acct_record_item {
+	struct fd_list		 chain;	/* link with all others */
+	struct fd_list		 unmapd;/* link with only unmap'd records */
+	struct acct_conf_avp 	*param;	/* the AVP entry this refers to. */
+	unsigned		 index;	/* in case of multi */
+	union avp_value		*value; /* If the AVP was found in the message, this points to its value. Otherwise, NULL */
+	union {
+		uint32_t v32	/* Storage area for network byte-order copy of the AVP value */;
+		uint64_t v64;
+		char	 c;	/* pointer that is passed to the database */
+	} 			 scalar;/* for scalar AVP (all types except OCTETSTRING) we copy in this area the value in network byte order */
+};
+
+/* The sentinel for a list of acct_record_items */
+struct acct_record_list {
+	struct fd_list	all;	/* The list of records */
+	int		nball;	/* The number of records in all */
+	struct fd_list	unmaped;/* Only the records without a value */
+	int		nbunmap;/* The number of unmap'd records */
+};
+
+/* Mapping of the data types between Diameter AVP and PQ types: */
+extern const char * diam2db_types_mapping[];
+
+/* In acct_conf.y */
+extern struct acct_conf * acct_config;	/* the global configuration */
+int acct_conf_init(void);
+int acct_conf_parse(char * conffile);
+int acct_conf_check(char * conffile);
+void acct_conf_free(void);
+
+/* In acct_db.c */
+int acct_db_init(void);
+int acct_db_insert(struct acct_record_list * records);
+void acct_db_free(void);
+
+/* In acct_records.c */
+int acct_rec_prepare(struct acct_record_list * records);
+int acct_rec_map(struct acct_record_list * records, struct msg * msg);
+int acct_rec_validate(struct acct_record_list * records);
+void acct_rec_empty(struct acct_record_list * records);