/*
 * datamodel.c - XMPP datamodel plugin
 *
 * Copyright (C) 2022, IOPSYS Software Solutions AB.
 *
 * Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
 *
 * See LICENSE file for license related information.
 *
 */

#include <libbbfdm-api/dmbbf.h>
#include <libbbfdm-api/dmcommon.h>
#include <libbbfdm-api/dmuci.h>

#include "datamodel.h"

#define XMPP_STATS_PATH	"/tmp/.xmppc_stats"

typedef struct xmpp_connection_data {
	char rx_msg[32];
	char rx_err_msg[32];
	char tx_msg[32];
	char tx_err_msg[32];
} connection_data;

/* ********** DynamicObj ********** */
DM_MAP_OBJ tDynamicObj[] = {
/* parentobj, nextobject, parameter */
{"Device.", tDeviceXMPPObj, NULL},
{"Device.ManagementServer.", NULL, tDeviceManagementServerParam},
{0}
};

/**************************************************************************
* COMMON METHOD
***************************************************************************/
static bool connection_alias_exist(const char *alias)
{
	bool exist = false;
	char *con_alias = NULL;
	struct uci_section *s = NULL, *stmp = NULL, *smap = NULL;

	uci_foreach_sections_safe("xmpp", "connection", stmp, s) {
		smap = get_dup_section_in_dmmap("dmmap_xmpp", "connection", section_name(s));
		if (!smap) {
			continue;
		}

		dmuci_get_value_by_section_string(smap, "con_alias", &con_alias);
		if (DM_STRCMP(con_alias, alias) == 0) {
			exist = true;
			break;
		}
	}

	return exist;
}

/*************************************************************
* ENTRY METHOD
**************************************************************/
/*#Device.XMPP.Connection.{i}.!UCI:xmpp/connection/dmmap_cwmp_xmpp*/
static int browseXMPPConnectionInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data *p;
	char *connection_name = NULL;
	char *con_alias = NULL;
	char *inst = NULL;
	LIST_HEAD(dup_list);

	dmuci_get_option_value_string("xmpp", "xmpp", "conn_req_connection", &connection_name);

	synchronize_specific_config_sections_with_dmmap("xmpp", "connection", "dmmap_xmpp", &dup_list);
	list_for_each_entry(p, &dup_list, list) {
		dmuci_get_value_by_section_string(p->dmmap_section, "con_alias", &con_alias);
		if (DM_STRLEN(con_alias) == 0) {
			dmuci_set_value_by_section(p->dmmap_section, "con_alias", section_name(p->config_section));
		}

		connection_data cd = {"0", "0", "0", "0"};

 		if (strcmp(connection_name, section_name(p->config_section)) == 0) {
			// cppcheck-suppress cert-MSC24-C
 			FILE *fp = fopen(XMPP_STATS_PATH, "r");
 			if (fp) {
 				fscanf(fp, "%31s %31s %31s %31s", cd.rx_msg, cd.tx_msg, cd.rx_err_msg, cd.tx_err_msg);
				fclose(fp);
			}
		}

		p->additional_data = (void *)(&cd);
		inst = handle_instance(dmctx, parent_node, p->dmmap_section, "con_inst", "con_alias");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p, inst) == DM_STOP)
			break;
	}
	free_dmmap_config_dup_list(&dup_list);
	return 0;

}

/*#Device.XMPP.Connection.{i}.!UCI:xmpp/connection_server/dmmap_cwmp_xmpp*/
static int browseXMPPConnectionServerInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct uci_section *connection_s = ((struct dm_data *)prev_data)->config_section;
	struct dm_data *p = NULL;
	char *inst = NULL;
	LIST_HEAD(dup_list);

	synchronize_specific_config_sections_with_dmmap_eq("xmpp", "connection_server", "dmmap_xmpp", "con_name", section_name(connection_s), &dup_list);
	list_for_each_entry(p, &dup_list, list) {

		inst = handle_instance(dmctx, parent_node, p->dmmap_section, "con_srv_inst", "con_srv_alias");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p, inst) == DM_STOP)
			break;
	}
	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

/*************************************************************
* ADD & DEL OBJ
**************************************************************/
static int addObjXMPPConnection(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section *s = NULL, *dmmap_s = NULL;
	char sec_name[32];

	snprintf(sec_name, sizeof(sec_name), "connection_%s", *instance);

	dmuci_add_section("xmpp", "connection", &s);
	dmuci_rename_section_by_section(s, sec_name);
	dmuci_set_value_by_section(s, "enable", "0");
	dmuci_set_value_by_section(s, "interval", "30");
	dmuci_set_value_by_section(s, "attempt", "16");
	dmuci_set_value_by_section(s, "serveralgorithm", "DNS-SRV");

	dmuci_add_section_bbfdm("dmmap_xmpp", "connection", &dmmap_s);
	dmuci_set_value_by_section(dmmap_s, "section_name", sec_name);
	dmuci_set_value_by_section(dmmap_s, "con_alias", sec_name);
	dmuci_set_value_by_section(dmmap_s, "con_inst", *instance);
	return 0;
}

static int delObjXMPPConnection(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	struct uci_section *connection_s = ((struct dm_data *)data)->config_section;
	struct uci_section *s = NULL, *dmmap_section = NULL, *stmp = NULL;
	
	uci_foreach_option_eq_safe("xmpp", "connection_server", "con_name", section_name(connection_s), stmp, s) {
		get_dmmap_section_of_config_section("dmmap_xmpp", "connection_server", section_name(s), &dmmap_section);
		dmuci_delete_by_section(dmmap_section, NULL, NULL);
		dmuci_delete_by_section(s, NULL, NULL);
	}

	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
	return 0;
}

static int addObjXMPPConnectionServer(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section *connection_s = ((struct dm_data *)data)->config_section;
	struct uci_section *s = NULL, *dmmap_s = NULL;
	char sec_name[64];

	snprintf(sec_name, sizeof(sec_name), "%s_srv_%s", section_name(connection_s), *instance);

	dmuci_add_section("xmpp", "connection_server", &s);
	dmuci_rename_section_by_section(s, sec_name);
	dmuci_set_value_by_section(s, "con_name", section_name(connection_s));
	dmuci_set_value_by_section(s, "enable", "0");
	dmuci_set_value_by_section(s, "priority", "0");
	dmuci_set_value_by_section(s, "weight", "-1");
	dmuci_set_value_by_section(s, "port", "5222");

	dmuci_add_section_bbfdm("dmmap_xmpp", "connection_server", &dmmap_s);
	dmuci_set_value_by_section(dmmap_s, "section_name", sec_name);
	dmuci_set_value_by_section(dmmap_s, "con_srv_inst", *instance);
	return 0;
}

static int delObjXMPPConnectionServer(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
	return 0;
}

/*************************************************************
* GET & SET PARAM
**************************************************************/
/*#Device.ManagementServer.ConnReqAllowedJabberIDs!UCI:xmpp/xmpp,xmpp/allowed_jid*/
static int get_management_server_conn_rep_allowed_jabber_id(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_option_value_string("xmpp", "xmpp", "allowed_jid", value);
	return 0;
}

static int set_management_server_conn_rep_allowed_jabber_id(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string_list(ctx, value, -1, 32, -1, -1, 256, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value("xmpp", "xmpp", "allowed_jid", value);
			return 0;
	}
	return 0;
}

static int get_management_server_conn_req_jabber_id(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *username = NULL;
	char *domain = NULL;
	char *resource = NULL;
	char *xmpp_connection = NULL;

	dmuci_get_option_value_string("xmpp", "xmpp", "conn_req_connection", &xmpp_connection);
	dmuci_get_option_value_string("xmpp", xmpp_connection, "username", &username);
	dmuci_get_option_value_string("xmpp", xmpp_connection, "domain", &domain);
	dmuci_get_option_value_string("xmpp", xmpp_connection, "resource", &resource);
	if (*username && *domain)
		dmasprintf(value, "%s@%s/%s", username, domain, resource);

	return 0;
}

static int get_management_server_conn_req_xmpp_connection(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *xmpp_connection = NULL, *xmpp_conn_alias = NULL;

	dmuci_get_option_value_string("xmpp", "xmpp", "conn_req_connection", &xmpp_connection);
	if (DM_STRLEN(xmpp_connection) == 0 || get_origin_section_from_config("xmpp", "connection", xmpp_connection) == NULL)
		return 0;

	struct uci_section *smap = get_dup_section_in_dmmap("dmmap_xmpp", "connection", xmpp_connection);
	dmuci_get_value_by_section_string(smap, "con_alias", &xmpp_conn_alias);
	if (DM_STRLEN(xmpp_conn_alias) == 0)
		xmpp_conn_alias = xmpp_connection;

	return _bbfdm_get_references(ctx, "Device.XMPP.Connection.", "Alias", xmpp_conn_alias, value);
}

static int set_management_server_conn_req_xmpp_connection(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.XMPP.Connection.", NULL};
	struct dm_reference reference = {0};
	struct uci_section *s = NULL, *stmp = NULL, *smap = NULL;
	char *con_alias = NULL, *section_name = NULL;

	bbfdm_get_reference_linker(ctx, value, &reference);

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, reference.path, -1, 256, NULL, NULL))
				return FAULT_9007;

			if (dm_validate_allowed_objects(ctx, &reference, allowed_objects))
				return FAULT_9007;

			return 0;
		case VALUESET:
			/* get section name for connection object */
			uci_foreach_sections_safe("xmpp", "connection", stmp, s) {
				smap = get_dup_section_in_dmmap("dmmap_xmpp", "connection", section_name(s));
				if (!smap) {
					continue;
				}

				dmuci_get_value_by_section_string(smap, "con_alias", &con_alias);
				if (DM_STRCMP(con_alias, reference.value) == 0) {
					section_name = section_name(s);
					break;
				}
			}

			if (section_name) {
				dmuci_set_value("xmpp", "xmpp", "conn_req_connection", section_name);
			}

			return 0;
	}
	return 0;
}

/*#Device.XMPP.ConnectionNumberOfEntries!UCI:xmpp/connection/*/
static int get_XMPP_ConnectionNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseXMPPConnectionInst);
	dmasprintf(value, "%d", cnt);
	return 0;
}

static int get_XMPP_SupportedServerConnectAlgorithms(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("DNS-SRV,DNS,ServerTable");
	return 0;
}

/*#Device.XMPP.Connection.{i}.Enable!UCI:xmpp/connection,@i-1/enable*/
static int get_XMPPConnection_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "enable", "1");
	return 0;
}

static int set_XMPPConnection_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			return 0;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "enable", b ? "1" : "0");
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.Alias!UCI:dmmap_xmpp/connection,@i-1/con_alias*/
static int get_XMPPConnection_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "con_alias", value);
	return 0;
}

static int set_XMPPConnection_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *cur_alias = NULL;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 64, NULL, NULL))
				return FAULT_9007;

			if (DM_STRLEN(value) == 0) {
				bbfdm_set_fault_message(ctx, "Alias value can't be empty");
				return FAULT_9007;
			}

			/* Alias value can't be duplicate */
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "con_alias", &cur_alias);
			if (DM_STRCMP(value, cur_alias) == 0)
				break;

			if (connection_alias_exist(value)) {
				bbfdm_set_fault_message(ctx, "Alias value can't be duplicate");
				return FAULT_9007;
			}

			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "con_alias", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.Username!UCI:xmpp/connection,@i-1/username*/
static int get_XMPPConnection_Username(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "username", value);
	return 0;
}

static int set_XMPPConnection_Username(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 256, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "username", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.Password!UCI:xmpp/connection,@i-1/password*/
static int get_XMPPConnection_Password(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "password", value);
	return 0;
}

static int set_XMPPConnection_Password(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 256, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "password", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.Domain!UCI:xmpp/connection,@i-1/domain*/
static int get_XMPPConnection_Domain(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "domain", value);
	return 0;
}

static int set_XMPPConnection_Domain(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 64, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "domain", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.Resource!UCI:xmpp/connection,@i-1/resource*/
static int get_XMPPConnection_Resource(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "resource", value);
	return 0;
}

static int set_XMPPConnection_Resource(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 64, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "resource", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.ServerConnectAlgorithm!UCI:xmpp/connection,@i-1/serveralgorithm*/
static int get_XMPPConnection_ServerConnectAlgorithm(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "serveralgorithm", value);
	return 0;
}

static int set_XMPPConnection_ServerConnectAlgorithm(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *ServerConnectAlgorithm[] = {"DNS-SRV", "DNS", "ServerTable", "WebSocket", NULL};

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, -1, ServerConnectAlgorithm, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "serveralgorithm", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.KeepAliveInterval!UCI:xmpp/connection,@i-1/interval*/
static int get_XMPPConnection_KeepAliveInterval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "interval", "-1");
	return 0;
}

static int set_XMPPConnection_KeepAliveInterval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_long(ctx, value, RANGE_ARGS{{"-1",NULL}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "interval", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.ServerConnectAttempts!UCI:xmpp/connection,@i-1/attempt*/
static int get_XMPPConnection_ServerConnectAttempts(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "attempt", "16");
	return 0;
}

static int set_XMPPConnection_ServerConnectAttempts(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,NULL}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "attempt", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.ServerRetryInitialInterval!UCI:xmpp/connection,@i-1/initial_retry_interval*/
static int get_XMPPConnection_ServerRetryInitialInterval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "initial_retry_interval", "60");
	return 0;
}

static int set_XMPPConnection_ServerRetryInitialInterval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"1","65535"}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "initial_retry_interval", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.ServerRetryIntervalMultiplier!UCI:xmpp/connection,@i-1/retry_interval_multiplier*/
static int get_XMPPConnection_ServerRetryIntervalMultiplier(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "retry_interval_multiplier", "2000");
	return 0;
}

static int set_XMPPConnection_ServerRetryIntervalMultiplier(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"1000","65535"}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "retry_interval_multiplier", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.ServerRetryMaxInterval!UCI:xmpp/connection,@i-1/retry_max_interval*/
static int get_XMPPConnection_ServerRetryMaxInterval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "retry_max_interval", "30720");
	return 0;
}

static int set_XMPPConnection_ServerRetryMaxInterval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"1",NULL}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "retry_max_interval", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.UseTLS!UCI:xmpp/connection,@i-1/usetls*/
static int get_XMPPConnection_UseTLS(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "usetls", "1");
	return 0;
}

static int set_XMPPConnection_UseTLS(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			return 0;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "usetls", b ? "1" : "0");
			return 0;
	}
	return 0;
}

static int get_XMPPConnection_JabberID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *username = NULL;
	char *domain = NULL;
	char *resource = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "username", &username);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "domain", &domain);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "resource", &resource);
	if (*username && *domain)
		dmasprintf(value, "%s@%s/%s", username, domain, resource);

	return 0;
}

static char *get_connection_status(struct uci_section *uci_sec)
{
	char *section = NULL, *status = NULL;

	if (!uci_sec)
		return dmstrdup("Error");

	dmuci_get_option_value_string_varstate("xmppc", "conn_status", "name", &section);
	if (DM_STRCMP(section, section_name(uci_sec)) == 0) {
		dmuci_get_option_value_string_varstate("xmppc", "conn_status", "status", &status);
		if (DM_STRLEN(status) != 0) {
			return dmstrdup(status);
		}
	}

	return dmstrdup("Unknown");
}

/*#Device.XMPP.Connection.{i}.Status!UCI:xmpp/connection,@i-1/enable*/
static int get_XMPPConnection_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *status;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "enable", &status);

	if (strcmp(status, "1") != 0) {
		*value = dmstrdup("Disabled");
	} else {
		*value = get_connection_status(((struct dm_data *)data)->config_section);
	}

	return 0;
}

static int get_XMPPConnection_LastChangeDate(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *section = NULL, *last_change = NULL;

	dmuci_get_option_value_string_varstate("xmppc", "conn_status", "name", &section);
	if (DM_STRCMP(section_name(((struct dm_data *)data)->config_section), section) == 0) {
		dmuci_get_option_value_string_varstate("xmppc", "conn_status", "last_change", &last_change);
		if (DM_STRLEN(last_change) != 0) {
			*value = dmstrdup(last_change);
			return 0;
		}
	}

	dmuci_get_option_value_string_varstate("xmppc", "global", "last_change", &last_change);
	if (DM_STRLEN(last_change) != 0) {
		*value = dmstrdup(last_change);
	}

	return 0;
}

static int get_XMPPConnection_TLSEstablished(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *use_tls = NULL, *status = NULL;

	get_XMPPConnection_UseTLS(refparam, ctx, data, instance, &use_tls);
	get_XMPPConnection_Status(refparam, ctx, data, instance, &status);

	if ((DM_STRCMP(use_tls, "1") == 0) && (DM_STRCMP(status, "Enabled") == 0))
		*value = dmstrdup("1");
	else
		*value = dmstrdup("0");

	return 0;
}

static int get_XMPPConnection_ServerNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseXMPPConnectionServerInst);
	dmasprintf(value, "%d", cnt);
	return 0;
}

/*#Device.XMPP.Connection.{i}.Server.{i}.Enable!UCI:xmpp/connection,@i-1/enable*/
static int get_XMPPConnectionServer_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "enable", "1");
	return 0;
}

static int set_XMPPConnectionServer_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			return 0;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "enable", b ? "1" : "0");
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.Server.{i}.Alias!UCI:dmmap_xmpp/connection_server,@i-1/con_srv_alias*/
static int get_XMPPConnectionServer_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "con_srv_alias", value);
	if ((*value)[0] == '\0')
		dmasprintf(value, "cpe-%s", instance);
	return 0;
}

static int set_XMPPConnectionServer_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 64, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "con_srv_alias", value);
			return 0;
	}
	return 0;
}

static int get_XMPPConnectionServer_Priority(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "priority", value);
	return 0;
}

static int set_XMPPConnectionServer_Priority(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"0","65535"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "priority", value);
			break;
	}
	return 0;
}

static int get_XMPPConnectionServer_Weight(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "weight", "-1");
	return 0;
}

static int set_XMPPConnectionServer_Weight(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_long(ctx, value, RANGE_ARGS{{"-1","65535"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "weight", value);
			break;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.Server.{i}.ServerAddress!UCI:xmpp/connection,@i-1/server_address*/
static int get_XMPPConnectionServer_ServerAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "server_address", value);
	return 0;
}

static int set_XMPPConnectionServer_ServerAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 256, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "server_address", value);
			return 0;
	}
	return 0;
}

/*#Device.XMPP.Connection.{i}.Server.{i}.Port!UCI:xmpp/connection,@i-1/port*/
static int get_XMPPConnectionServer_Port(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "port", "5222");
	return 0;
}

static int set_XMPPConnectionServer_Port(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"0","65535"}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "port", value);
			return 0;
	}
	return 0;
}

static int get_XMPPConnectionStats_ReceivedMessages(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (data) {
		connection_data *cd = (connection_data *)((struct dm_data *)data)->additional_data;
		*value = cd ? dmstrdup(cd->rx_msg) : "";
	}
	return 0;
}

static int get_XMPPConnectionStats_TransmittedMessages(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (data) {
		connection_data *cd = (connection_data *)((struct dm_data *)data)->additional_data;
		*value = cd ? dmstrdup(cd->tx_msg) : "";
	}
	return 0;
}

static int get_XMPPConnectionStats_ReceivedErrorMessages(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (data) {
		connection_data *cd = (connection_data *)((struct dm_data *)data)->additional_data;
		*value = cd ? dmstrdup(cd->rx_err_msg) : "";
	}
	return 0;
}

static int get_XMPPConnectionStats_TransmittedErrorMessages(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (data) {
		connection_data *cd = (connection_data *)((struct dm_data *)data)->additional_data;
		*value = cd ? dmstrdup(cd->tx_err_msg) : "";
	}
	return 0;
}

/**********************************************************************************************************************************
*                                            OBJ & LEAF DEFINITION
***********************************************************************************************************************************/
/* *** Device.ManagementServer. *** */
DMLEAF tDeviceManagementServerParam[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */
{"ConnReqAllowedJabberIDs", &DMWRITE, DMT_STRING, get_management_server_conn_rep_allowed_jabber_id, set_management_server_conn_rep_allowed_jabber_id, BBFDM_CWMP},
{"ConnReqJabberID", &DMREAD, DMT_STRING, get_management_server_conn_req_jabber_id, NULL, BBFDM_CWMP},
{"ConnReqXMPPConnection", &DMWRITE, DMT_STRING, get_management_server_conn_req_xmpp_connection, set_management_server_conn_req_xmpp_connection, BBFDM_CWMP, DM_FLAG_REFERENCE},
{0}
};

/* *** Device.XMPP. *** */
DMOBJ tDeviceXMPPObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys */
{"XMPP", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tXMPPObj, tXMPPParams, NULL, BBFDM_CWMP},
{0}
};

DMOBJ tXMPPObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys */
{"Connection", &DMWRITE, addObjXMPPConnection, delObjXMPPConnection, NULL, browseXMPPConnectionInst, NULL, NULL, tXMPPConnectionObj, tXMPPConnectionParams, NULL, BBFDM_CWMP},
{0}
};

DMLEAF tXMPPParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */
{"ConnectionNumberOfEntries", &DMREAD, DMT_UNINT, get_XMPP_ConnectionNumberOfEntries, NULL, BBFDM_CWMP},
{"SupportedServerConnectAlgorithms", &DMREAD, DMT_STRING, get_XMPP_SupportedServerConnectAlgorithms, NULL, BBFDM_CWMP},
{0}
};

/* *** Device.XMPP.Connection.{i}. *** */
DMOBJ tXMPPConnectionObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys */
{"Server", &DMWRITE, addObjXMPPConnectionServer, delObjXMPPConnectionServer, NULL, browseXMPPConnectionServerInst, NULL, NULL, NULL, tXMPPConnectionServerParams, NULL, BBFDM_CWMP},
{"Stats", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tXMPPConnectionStatsParams, NULL, BBFDM_CWMP, NULL},
{0}
};

DMLEAF tXMPPConnectionParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */
{"Enable", &DMWRITE, DMT_BOOL, get_XMPPConnection_Enable, set_XMPPConnection_Enable, BBFDM_CWMP},
{"Alias", &DMWRITE, DMT_STRING, get_XMPPConnection_Alias, set_XMPPConnection_Alias, BBFDM_CWMP, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
{"Username", &DMWRITE, DMT_STRING, get_XMPPConnection_Username, set_XMPPConnection_Username, BBFDM_CWMP, DM_FLAG_UNIQUE},
{"Password", &DMWRITE, DMT_STRING, get_XMPPConnection_Password, set_XMPPConnection_Password, BBFDM_CWMP, DM_FLAG_SECURE},
{"Domain", &DMWRITE, DMT_STRING, get_XMPPConnection_Domain, set_XMPPConnection_Domain, BBFDM_CWMP, DM_FLAG_UNIQUE},
{"Resource", &DMWRITE, DMT_STRING, get_XMPPConnection_Resource, set_XMPPConnection_Resource, BBFDM_CWMP, DM_FLAG_UNIQUE},
{"JabberID", &DMREAD, DMT_STRING, get_XMPPConnection_JabberID, NULL, BBFDM_CWMP},
{"Status", &DMREAD, DMT_STRING, get_XMPPConnection_Status, NULL, BBFDM_CWMP},
{"LastChangeDate", &DMREAD, DMT_TIME, get_XMPPConnection_LastChangeDate, NULL, BBFDM_CWMP},
{"ServerConnectAlgorithm", &DMWRITE, DMT_STRING, get_XMPPConnection_ServerConnectAlgorithm, set_XMPPConnection_ServerConnectAlgorithm, BBFDM_CWMP},
{"KeepAliveInterval", &DMWRITE, DMT_LONG, get_XMPPConnection_KeepAliveInterval, set_XMPPConnection_KeepAliveInterval, BBFDM_CWMP},
{"ServerConnectAttempts", &DMWRITE, DMT_UNINT, get_XMPPConnection_ServerConnectAttempts, set_XMPPConnection_ServerConnectAttempts, BBFDM_CWMP},
{"ServerRetryInitialInterval", &DMWRITE, DMT_UNINT, get_XMPPConnection_ServerRetryInitialInterval, set_XMPPConnection_ServerRetryInitialInterval, BBFDM_CWMP},
{"ServerRetryIntervalMultiplier", &DMWRITE, DMT_UNINT, get_XMPPConnection_ServerRetryIntervalMultiplier, set_XMPPConnection_ServerRetryIntervalMultiplier, BBFDM_CWMP},
{"ServerRetryMaxInterval", &DMWRITE, DMT_UNINT, get_XMPPConnection_ServerRetryMaxInterval, set_XMPPConnection_ServerRetryMaxInterval, BBFDM_CWMP},
{"UseTLS", &DMWRITE, DMT_BOOL, get_XMPPConnection_UseTLS, set_XMPPConnection_UseTLS, BBFDM_CWMP},
{"TLSEstablished", &DMREAD, DMT_BOOL, get_XMPPConnection_TLSEstablished, NULL, BBFDM_CWMP},
{"ServerNumberOfEntries", &DMREAD, DMT_UNINT, get_XMPPConnection_ServerNumberOfEntries, NULL, BBFDM_CWMP},
{0}
};

/* *** Device.XMPP.Connection.{i}.Server.{i}. *** */
DMLEAF tXMPPConnectionServerParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */
{"Enable", &DMWRITE, DMT_BOOL, get_XMPPConnectionServer_Enable, set_XMPPConnectionServer_Enable, BBFDM_CWMP},
{"Alias", &DMWRITE, DMT_STRING, get_XMPPConnectionServer_Alias, set_XMPPConnectionServer_Alias, BBFDM_CWMP, DM_FLAG_UNIQUE},
{"Priority", &DMWRITE, DMT_UNINT, get_XMPPConnectionServer_Priority, set_XMPPConnectionServer_Priority, BBFDM_CWMP},
{"Weight", &DMWRITE, DMT_LONG, get_XMPPConnectionServer_Weight, set_XMPPConnectionServer_Weight, BBFDM_CWMP},
{"ServerAddress", &DMWRITE, DMT_STRING, get_XMPPConnectionServer_ServerAddress, set_XMPPConnectionServer_ServerAddress, BBFDM_CWMP, DM_FLAG_UNIQUE},
{"Port", &DMWRITE, DMT_UNINT, get_XMPPConnectionServer_Port, set_XMPPConnectionServer_Port, BBFDM_CWMP, DM_FLAG_UNIQUE},
{0}
};

/* *** Device.XMPP.Connection.{i}.Stats. *** */
DMLEAF tXMPPConnectionStatsParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */
{"ReceivedMessages", &DMREAD, DMT_UNINT, get_XMPPConnectionStats_ReceivedMessages, NULL, BBFDM_CWMP},
{"TransmittedMessages", &DMREAD, DMT_UNINT, get_XMPPConnectionStats_TransmittedMessages, NULL, BBFDM_CWMP},
{"ReceivedErrorMessages", &DMREAD, DMT_UNINT, get_XMPPConnectionStats_ReceivedErrorMessages, NULL, BBFDM_CWMP},
{"TransmittedErrorMessages", &DMREAD, DMT_UNINT, get_XMPPConnectionStats_TransmittedErrorMessages, NULL, BBFDM_CWMP},
{0}
};
