Index: extensions/app_acct/CMakeLists.txt
===================================================================
--- extensions/app_acct/CMakeLists.txt	(revision 284)
+++ extensions/app_acct/CMakeLists.txt	(revision 285)
@@ -17,9 +17,11 @@
 	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
-	acct_db.c
-	acct_records.c
 )
 
Index: extensions/app_acct/acct_db.c
===================================================================
--- extensions/app_acct/acct_db.c	(revision 284)
+++ extensions/app_acct/acct_db.c	(revision 285)
@@ -51,6 +51,10 @@
 
 static const char * stmt = "acct_db_stmt";
-static PGconn *conn = NULL;
-
+#ifndef TEST_DEBUG
+static 
+#endif /* TEST_DEBUG */
+PGconn *conn = NULL;
+
+/* Initialize the database context: connection to the DB, prepared statement to insert new records */
 int acct_db_init(void)
 {
@@ -63,5 +67,5 @@
 	int idx = 0;
 	PGresult * res;
-	#define REALLOC_SIZE	1024
+	#define REALLOC_SIZE	1024	/* We extend the buffer by this amount */
 	
 	TRACE_ENTRY();
@@ -76,7 +80,12 @@
 		acct_db_free();
 		return EINVAL;
-	} else {
-		TRACE_DEBUG(INFO, "Connection to database successfull: user:%s, db:%s, host:%s.", PQuser(conn), PQdb(conn), PQhost(conn));
-	}
+	}
+	if (PQprotocolVersion(conn) < 3) {
+		fd_log_debug("Database protocol version is too old, version 3 is required for prepared statements.\n");
+		acct_db_free();
+		return EINVAL;
+	}
+	
+	TRACE_DEBUG(FULL, "Connection to database successful, server version %d.", PQserverVersion(conn));
 	
 	/* Now, prepare the request object */
@@ -174,19 +183,103 @@
 	PQclear(res);
 	
-	
-
+	free(sql);
+	acct_rec_empty(&emptyrecords);
+	
+	/* Ok, ready */
 	return 0;
 }
 
+/* Terminate the connection to the DB */
 void acct_db_free(void)
-{
-	if (conn)
+{	
+	if (conn) {
+		/* Note: the prepared statement is automatically freed when the session terminates */
 		PQfinish(conn);
-	conn = NULL;
+		conn = NULL;
+	}
 }
 
+/* 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)
 {
-	return ENOTSUP;
+	char 	**val;
+	int	 *val_len;
+	int 	 *val_isbin;
+	int	  idx = 0;
+	PGresult *res;
+	struct fd_list *li;
+	
+	TRACE_ENTRY("%p", records);
+	CHECK_PARAMS( conn && records );
+	
+	/* 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 completly?");
+			return ENOTCONN;
+		}
+	}
+	
+	/* Alloc the arrays of parameters */
+	CHECK_MALLOC( val       = calloc(records->nball, sizeof(const char *)) );
+	CHECK_MALLOC( val_len   = calloc(records->nball, sizeof(const int)) );
+	CHECK_MALLOC( val_isbin = calloc(records->nball, sizeof(const int)) );
+	
+	/* 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, records->nball, (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;
 }
 
Index: extensions/app_acct/acct_records.c
===================================================================
--- extensions/app_acct/acct_records.c	(revision 284)
+++ extensions/app_acct/acct_records.c	(revision 285)
@@ -74,10 +74,88 @@
 }
 
+/* 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) );
+	}
 	
-	return ENOTSUP;
+	/* 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.\n"
+				"This is an invalid situation. Please fix your configuration file.\n"
+				"One way to ensure this does not happen is to include Session-Id in the database.\n");
+		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'.\n", 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);
+	}
+}
Index: extensions/app_acct/app_acct.c
===================================================================
--- extensions/app_acct/app_acct.c	(revision 284)
+++ extensions/app_acct/app_acct.c	(revision 285)
@@ -38,14 +38,9 @@
 #include "app_acct.h"
 
-/* Default callback for the Accounting application. */
-static int acct_fallback( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act)
-{
-	/* This CB should never be called */
-	TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act);
-	
-	fd_log_debug("Unexpected message received!\n");
-	
-	return ENOTSUP;
-}
+/* Mandatory AVPs for the Accounting-Answer */
+static struct {
+	struct dict_object * Accounting_Record_Number;
+	struct dict_object * Accounting_Record_Type;
+} acct_dict;
 
 
@@ -54,8 +49,9 @@
 {
 	struct msg_hdr *hdr = NULL;
-	struct msg *ans, *qry;
+	struct msg * m;
 	struct avp * a = NULL;
-	struct avp_hdr * h = NULL;
+	struct avp_hdr * art=NULL, *arn=NULL; /* We keep a pointer on the Accounting-Record-{Type, Number} AVPs from the query */
 	char * s;
+	struct acct_record_list rl;
 	
 	TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act);
@@ -63,24 +59,53 @@
 		return EINVAL;
 	
-	qry = *msg;
-	/* Create the answer message, including the Session-Id AVP */
+	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 ) );
-	ans = *msg;
+	m = *msg;
 
 	/* Set the Origin-Host, Origin-Realm, Result-Code AVPs */
-	CHECK_FCT( fd_msg_rescode_set( ans, "DIAMETER_SUCCESS", NULL, NULL, 1 ) );
-
-	fd_log_debug("--------------Received the following Accounting message:--------------\n");
-
-	CHECK_FCT( fd_sess_getsid ( sess, &s ) );
-	fd_log_debug("Session: %s\n", s);
-
-	/* We may also dump other data from the message, such as Accounting session Id, number of packets, ...  */
-
-	fd_log_debug("----------------------------------------------------------------------\n");
-
+	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 */
-	CHECK_FCT( fd_msg_send( msg, NULL, NULL ) );
-		
+	*act = DISP_ACT_SEND;
 	return 0;
 }
@@ -94,16 +119,21 @@
 	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_disp_register( acct_fallback, DISP_HOW_APPID, &data, NULL ) );
 	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 ) );
Index: extensions/app_acct/app_acct.h
===================================================================
--- extensions/app_acct/app_acct.h	(revision 284)
+++ extensions/app_acct/app_acct.h	(revision 285)
@@ -84,4 +84,9 @@
 	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 */
 };
 
@@ -112,2 +117,4 @@
 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);
