/*
 * Copyright (C) 2020 iopsys Software Solutions AB
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation
 *
 *	Author: Yalu Zhang, yalu.zhang@iopsys.eu
 */

#include "servicesvoiceservice.h"
#include "servicesvoiceservicesip.h"
#include "common.h"

/*************************************************************
* COMMON FUNCTIONS
**************************************************************/
static void update_sip_configuration(struct uci_section *s_from, struct uci_section *s_to)
{
	char *enable = NULL;
	char *port = NULL;
	char *transport = NULL;
	char *domain = NULL;
	char *outbound_proxy = NULL;
	char *outbound_proxy_port = NULL;
	char *defaultexpiry = NULL;
	char *voipprofile = NULL;
	char *codecs = NULL;

	if (s_to == NULL)
		return;

	if (s_from == NULL) {
		dmuci_set_value_by_section(s_to, "enable", "");
		dmuci_set_value_by_section(s_to, "port", "");
		dmuci_set_value_by_section(s_to, "transport", "");
		dmuci_set_value_by_section(s_to, "domain", "");
		dmuci_set_value_by_section(s_to, "outbound_proxy", "");
		dmuci_set_value_by_section(s_to, "outbound_proxy_port", "");
		dmuci_set_value_by_section(s_to, "defaultexpiry", "");
		dmuci_set_value_by_section(s_to, "voipprofile", "");
		dmuci_set_value_by_section(s_to, "codecs", "");
		return;
	}

	dmuci_get_value_by_section_string(s_from, "enable", &enable);
	dmuci_get_value_by_section_string(s_from, "port", &port);
	dmuci_get_value_by_section_string(s_from, "transport", &transport);
	dmuci_get_value_by_section_string(s_from, "domain", &domain);
	dmuci_get_value_by_section_string(s_from, "outbound_proxy", &outbound_proxy);
	dmuci_get_value_by_section_string(s_from, "outbound_proxy_port", &outbound_proxy_port);
	dmuci_get_value_by_section_string(s_from, "defaultexpiry", &defaultexpiry);
	dmuci_get_value_by_section_string(s_from, "voipprofile", &voipprofile);
	dmuci_get_value_by_section_string(s_from, "codecs", &codecs);

	if (DM_STRLEN(enable))
		dmuci_set_value_by_section(s_to, "enable", enable);

	if (DM_STRLEN(port))
		dmuci_set_value_by_section(s_to, "port", port);

	if (DM_STRLEN(transport))
		dmuci_set_value_by_section(s_to, "transport", transport);

	if (DM_STRLEN(domain))
		dmuci_set_value_by_section(s_to, "domain", domain);

	if (DM_STRLEN(outbound_proxy))
		dmuci_set_value_by_section(s_to, "outbound_proxy", outbound_proxy);

	if (DM_STRLEN(outbound_proxy_port))
		dmuci_set_value_by_section(s_to, "outbound_proxy_port", outbound_proxy_port);

	if (DM_STRLEN(defaultexpiry))
		dmuci_set_value_by_section(s_to, "defaultexpiry", defaultexpiry);

	if (DM_STRLEN(voipprofile))
		dmuci_set_value_by_section(s_to, "voipprofile", voipprofile);

	if (DM_STRLEN(codecs))
		dmuci_set_value_by_section(s_to, "codecs", codecs);
}

void dmmap_synchronize_SIP_Network(struct list_head *dup_list)
{
	struct uci_section *s = NULL, *ss = NULL, *stmp = NULL;
	char *user_s = NULL, *sip_client = NULL;

	uci_path_foreach_sections_safe(bbfdm, "dmmap_asterisk", "sip_network", stmp, s) {

		// section added by user ==> skip it
		dmuci_get_value_by_section_string(s, "added_by_user", &user_s);
		if (DM_LSTRCMP(user_s, "1") == 0)
			continue;

		// check config section ==> if it exists then skip it
		dmuci_get_value_by_section_string(s, "sip_client", &sip_client);
		if (DM_STRLEN(sip_client)) {
			ss = get_origin_section_from_config("asterisk", "sip_service_provider", sip_client);
			if (ss)
				continue;
		}

		// else ==> delete section
		dmuci_delete_by_section(s, NULL, NULL);
	}

	uci_foreach_sections("asterisk", "sip_service_provider", s) {

		// if dmmap section exits ==> skip it
		ss = get_dup_section_in_dmmap_opt("dmmap_asterisk", "sip_network", "sip_client", section_name(s));
		if (ss)
			continue;

		// section added by user ==> skip it
		ss = get_dup_section_in_dmmap("dmmap_asterisk", "sip_service_provider", section_name(s));
		dmuci_get_value_by_section_string(ss, "added_by_user", &user_s);
		if (DM_LSTRCMP(user_s, "1") == 0)
			continue;

		// Create a new dmmap sip_network section
		dmuci_add_section_bbfdm("dmmap_asterisk", "sip_network", &ss);
		dmuci_set_value_by_section(ss, "sip_client", section_name(s));

		// Update sip_network configuration
		update_sip_configuration(s, ss);
	}

	uci_path_foreach_sections_safe(bbfdm, "dmmap_asterisk", "sip_network", stmp, s) {
		struct uci_section *config_s = NULL;

		dmuci_get_value_by_section_string(s, "sip_client", &sip_client);
		if (DM_STRLEN(sip_client))
			config_s = get_origin_section_from_config("asterisk", "sip_service_provider", sip_client);

		/* Add system and dmmap sections to the list */
		add_dmmap_config_dup_list(dup_list, config_s, s);
	}
}

/*************************************************************
* ENTRY METHOD
**************************************************************/
/*#Device.Services.VoiceService.{i}.SIP.Client.{i}.!UCI:asterisk/sip_service_provider/dmmap_asterisk*/
static int browseServicesVoiceServiceSIPClientInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	return browseVoiceServiceSIPProviderInst(dmctx, parent_node, prev_data, prev_instance);
}

/*#Device.Services.VoiceService.{i}.SIP.Client.{i}.Contact.{i}.*/
/*
static int browseServicesVoiceServiceSIPClientContactInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	// prev_data is from its parent node SIP.Client.{i}. i.e. the UCI section of asterisk.sip_service_provider
	DM_LINK_INST_OBJ(dmctx, parent_node, prev_data, "1");
	return 0;
}
*/

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.!UCI:asterisk/sip_service_provider/dmmap_asterisk*/
static int browseServicesVoiceServiceSIPNetworkInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *inst = NULL;
	struct dm_data *p = NULL;
	LIST_HEAD(dup_list);

	dmmap_synchronize_SIP_Network(&dup_list);

	list_for_each_entry(p, &dup_list, list) {

		inst = handle_instance(dmctx, parent_node, p->dmmap_section, "networkinstance", "networkalias");

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

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.FQDNServer.{i}.*/
static int browseServicesVoiceServiceSIPNetworkFQDNServerInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	// prev_data is from its parent node SIP.Network.{i}. i.e. a UCI section of asterisk.sip_service_provider
	DM_LINK_INST_OBJ(dmctx, parent_node, prev_data, "1");
	return 0;
}

/*************************************************************
* ADD & DEL OBJ
**************************************************************/
static int addObjServicesVoiceServiceSIPClient(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	char new_sec_name[16], value[32];
	struct uci_section *dmmap = NULL;

	snprintf(new_sec_name, sizeof(new_sec_name), "sip%s", *instance);
	snprintf(value, sizeof(value), "account %s", *instance);

	dmuci_set_value(TR104_UCI_PACKAGE, new_sec_name, "", "sip_service_provider");
	dmuci_set_value(TR104_UCI_PACKAGE, new_sec_name, "name", value);
	dmuci_set_value(TR104_UCI_PACKAGE, new_sec_name, "enable", "0");
	dmuci_set_value(TR104_UCI_PACKAGE, new_sec_name, "support_fax", "0");
	dmuci_set_value(TR104_UCI_PACKAGE, new_sec_name, "transport", "udp");

	dmuci_add_section_bbfdm("dmmap_asterisk", "sip_service_provider", &dmmap);
	dmuci_set_value_by_section(dmmap, "section_name", new_sec_name);
	dmuci_set_value_by_section(dmmap, "added_by_user", "1");
	dmuci_set_value_by_section(dmmap, "clientinstance", *instance);
	return 0;
}

static int delObjServicesVoiceServiceSIPClient(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	return delObjVoiceServiceSIPProvider(refparam, ctx, data, instance, del_action);
}

static int addObjServicesVoiceServiceSIPNetwork(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section *dmmap = NULL;

	dmuci_add_section_bbfdm("dmmap_asterisk", "sip_network", &dmmap);
	dmuci_set_value_by_section(dmmap, "enable", "0");
	dmuci_set_value_by_section(dmmap, "added_by_user", "1");
	dmuci_set_value_by_section(dmmap, "networkinstance", *instance);
	return 0;
}

static int delObjServicesVoiceServiceSIPNetwork(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	switch (del_action) {
		case DEL_INST:
			if (((struct dm_data *)data)->config_section) {
				update_sip_configuration(NULL, ((struct dm_data *)data)->config_section);

				struct uci_section *dmmap_s = get_dup_section_in_dmmap("dmmap_asterisk", "sip_service_provider", section_name(((struct dm_data *)data)->config_section));
				dmuci_set_value_by_section(dmmap_s, "added_by_user", "1");
			}

			dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
			break;
		case DEL_ALL:
			break;
	}
	return 0;
}

/*
static int addObjServicesVoiceServiceSIPClientContact(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	//TODO
	return 0;
}

static int delObjServicesVoiceServiceSIPClientContact(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	switch (del_action) {
		case DEL_INST:
			//TODO
			break;
		case DEL_ALL:
			//TODO
			break;
	}
	return 0;
}

static int addObjServicesVoiceServiceSIPNetworkFQDNServer(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	//TODO
	return 0;
}

static int delObjServicesVoiceServiceSIPNetworkFQDNServer(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	switch (del_action) {
		case DEL_INST:
			//TODO
			break;
		case DEL_ALL:
			//TODO
			break;
	}
	return 0;
}
*/

/*************************************************************
* GET & SET PARAM
**************************************************************/
/*#Device.Services.VoiceService.{i}.SIP.NetworkNumberOfEntries*/
static int get_ServicesVoiceServiceSIP_NetworkNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmasprintf(value, "%d", get_number_of_entries(ctx, data, instance, browseServicesVoiceServiceSIPNetworkInst));
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.ClientNumberOfEntries*/
static int get_ServicesVoiceServiceSIP_ClientNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmasprintf(value, "%d", get_number_of_entries(ctx, data, instance, browseServicesVoiceServiceSIPClientInst));
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Client.{i}.Enable!UCI:asterisk/sip_service_provider,@i-1/enable*/
static int get_ServicesVoiceServiceSIPClient_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_ServicesVoiceServiceSIPClient_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;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "enable", b ? "1" : "0");
			break;
	}
	return 0;
}

static int get_ServicesVoiceServiceSIPClient_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *section = ((struct dm_data *)data)->config_section;
	char *enabled = NULL;

	dmuci_get_value_by_section_string(section, "enable", &enabled);
	if (enabled && *enabled == '0') {
		*value = dmstrdup("Disabled");

		dmfree(enabled);
	} else {
		// Get registration status from ubus
		json_object *res = NULL, *sip = NULL, *client = NULL;

		dmubus_call("voice.asterisk", "status", UBUS_ARGS{0}, 0, &res);
		if (res) {
			sip = dmjson_get_obj(res, 1, "sip");
			if (sip) {
				client = dmjson_get_obj(sip, 1, section->e.name);
				if (client) {
					char *state = dmjson_get_value(client, 1, "state");
					if (state && *state) {
						if (strcasecmp(state, "Registered") == 0) {
							*value = dmstrdup("Up");
						} else if (strcasecmp(state, "Rejected") == 0) {
							*value = dmstrdup("Error_Registration");
						} else if (strcasecmp(state, "Stopped") == 0) {
							*value = dmstrdup("Quiescent");
						} else if (strcasecmp(state, "Unregistered") == 0) {
							*value = dmstrdup("Registering");
						}
					}
				}
			}
		} else {
			BBF_DEBUG("dmubus_call() failed\n");
		}
	}

	// For internal failure
	if (!*value || !**value)
		*value = dmstrdup("Error_Registration");

	return 0;
}

static int get_ServicesVoiceServiceSIPClient_Origin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("Static");
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Client.{i}.AuthUserName!UCI:asterisk/sip_service_provider,@i-1/authuser*/
static int get_ServicesVoiceServiceSIPClient_AuthUserName(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "authuser", value);
	return 0;
}

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

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

/*#Device.Services.VoiceService.{i}.SIP.Client.{i}.RegisterURI!UCI:asterisk/sip_service_provider,@i-1/user*/
static int get_ServicesVoiceServiceSIPClient_RegisterURI(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *value_user = NULL;
	char *value_address = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "user", &value_user);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "domain", &value_address);

	if (!(value_address && *value_address)) {
		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "outbound_proxy", &value_address);
	}

	dmasprintf(value, "%s@%s", value_user, value_address);
	return 0;
}

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

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 389, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			value_domain = DM_STRCHR(value, '@');
			if (value_domain) {
				value_domain++;
				value_user = dmstrdup(value);
				if (value_user) {
					value_user[value_domain - value - 1] = '\0';
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "user", value_user);
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "domain", value_domain);
				}
			}
			break;
	}
	return 0;
}

static int get_ServicesVoiceServiceSIPClient_Network(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "LowerLayers", value);

	if ((*value)[0] == '\0') {
		char *alias = NULL;

		struct uci_section *sip_network_s = get_dup_section_in_dmmap_opt("dmmap_asterisk", "sip_network",
				"sip_client", section_name(((struct dm_data *)data)->config_section));

		dmuci_get_value_by_section_string(sip_network_s, "networkalias", &alias);

		_bbfdm_get_references(ctx, "Device.Services.VoiceService.1.SIP.Network.", "Alias", alias, value);

		// Store LowerLayers value
		dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "LowerLayers", *value);
	} else {
		if (!adm_entry_object_exists(ctx, *value))
			*value = dmstrdup("");
	}

	return 0;
}

static int set_ServicesVoiceServiceSIPClient_Network(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.Services.VoiceService.1.SIP.Network.", NULL};
	struct dm_reference reference = {0};
	struct uci_section *s = NULL;
	char *prev_net = NULL;

	bbfdm_get_reference_linker(ctx, value, &reference);

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

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

			if (DM_STRLEN(reference.path)) {
				char *sip_client = NULL;

				s = get_dup_section_in_dmmap_opt("dmmap_asterisk", "sip_network", "networkalias", reference.value);

				dmuci_get_value_by_section_string(s, "sip_client", &sip_client);
				if (DM_STRLEN(sip_client)) {
					bbfdm_set_fault_message(ctx, "'%s' is already used by another SIP Client !!!", reference.path);
					return FAULT_9007;
				}
			}

			break;
		case VALUESET:
			// Store LowerLayers value under dmmap section
			get_ServicesVoiceServiceSIPClient_Network(refparam, ctx, data, instance, &prev_net);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "LowerLayers", reference.path);

			s = get_dup_section_in_dmmap_opt("dmmap_asterisk", "sip_network", "sip_client", section_name(((struct dm_data *)data)->config_section));
			dmuci_set_value_by_section(s, "added_by_user", "1");
			dmuci_set_value_by_section(s, "sip_client", "");

			if (DM_STRLEN(prev_net) != 0) {
				update_sip_configuration(NULL, ((struct dm_data *)data)->config_section);
			}

			if (DM_STRLEN(reference.path)) {
				s = get_dup_section_in_dmmap_opt("dmmap_asterisk", "sip_network", "networkalias", reference.value);

				dmuci_set_value_by_section(s, "sip_client", section_name(((struct dm_data *)data)->config_section));

				update_sip_configuration(s, ((struct dm_data *)data)->config_section);
			}
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.Enable!UCI:asterisk/sip_service_provider,@i-1/enable*/
static int get_ServicesVoiceServiceSIPNetwork_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "enable", "1");
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_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;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "enable", b ? "1" : "0");
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "enable", b ? "1" : "0");
			break;
	}
	return 0;
}

static int get_ServicesVoiceServiceSIPNetwork_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("Up");
	return 0;
}

static int get_server_address(struct dm_data *data, char *option, char **value)
{
	dmuci_get_value_by_section_string(data->dmmap_section, option, value);
	if (*value && **value) {
		char *port = DM_STRCHR(*value, ':');
		if (port) {
			char *server = dmstrdup(*value);
			if (server) {
				server[port - *value] = '\0';
				dmfree(*value);
				*value = server;
			}
		}
	}

	return 0;
}

static int set_server_address(struct dm_data *data, char *option, char *value)
{
	char *old_value = NULL;

	dmuci_get_value_by_section_string(data->dmmap_section, option, &old_value);
	char *port = (old_value && *old_value) ? DM_STRCHR(old_value, ':') : NULL;
	if (port) {
		char new_value[32] = {0};

		port++;
		snprintf(new_value, sizeof(new_value), "%s:%s", value, port);
		dmuci_set_value_by_section(data->config_section, option, new_value);
		dmuci_set_value_by_section(data->dmmap_section, option, new_value);
	} else {
		dmuci_set_value_by_section(data->config_section, option, value);
		dmuci_set_value_by_section(data->dmmap_section, option, value);
	}

	if (old_value && *old_value)
		dmfree(old_value);

	return 0;
}

static int get_server_port(struct dm_data *data, char *option, char **value)
{
	char *domain = NULL, *port = NULL;

	dmuci_get_value_by_section_string(data->dmmap_section, option, &domain);
	if (domain && *domain) {
		port = DM_STRCHR(domain, ':');
		if (port)
			port++;
	}

	*value = dmstrdup((port && *port) ? port : DEFAULT_SIP_PORT_STR);

	if (domain && *domain)
		dmfree(domain);

	return 0;
}

static int set_server_port(struct dm_data *data, char *option, char *value)
{
	char *old_value = NULL, new_value[32] = {0};

	dmuci_get_value_by_section_string(data->dmmap_section, option, &old_value);
	char *tmp = old_value ? DM_STRCHR(old_value, ':') : NULL;
	if (tmp)
		*tmp = '\0';

	snprintf(new_value, sizeof(new_value), "%s:%s", old_value ? old_value : "", value);
	dmuci_set_value_by_section(data->config_section, option, new_value);
	dmuci_set_value_by_section(data->dmmap_section, option, new_value);

	if (old_value && *old_value)
		dmfree(old_value);

	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.ProxyServer!UCI:asterisk/sip_service_provider,@i-1/outbound_proxy*/
static int get_ServicesVoiceServiceSIPNetwork_ProxyServer(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "outbound_proxy", value);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_ProxyServer(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;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "outbound_proxy", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "outbound_proxy", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.ProxyServerPort!UCI:asterisk/sip_service_provider,@i-1/port*/
static int get_ServicesVoiceServiceSIPNetwork_ProxyServerPort(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "port", DEFAULT_SIP_PORT_STR);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_ProxyServerPort(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, "port", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "port", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.ProxyServerTransport!UCI:asterisk/sip_service_provider,@i-1/transport*/
/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.UserAgentTransport!UCI:asterisk/sip_service_provider,@i-1/transport*/
static int get_ServicesVoiceServiceSIPNetwork_Transport(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "transport", value);
	if (*value && **value) {
		// Convert to uppercase
		for (char *ch = *value; *ch != '\0'; ch++)
			*ch = toupper(*ch);
	} else {
		*value = dmstrdup("UDP");
	}
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_Transport(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, -1, ProxyServerTransport, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			for (char *ch = value; *ch != '\0'; ch++)
				*ch = tolower(*ch);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "transport", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "transport", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.RegistrarServer!UCI:asterisk/sip_service_provider,@i-1/outbound_proxy*/
static int get_ServicesVoiceServiceSIPNetwork_RegistrarServer(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "outbound_proxy", value);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_RegistrarServer(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;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "outbound_proxy", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "outbound_proxy", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.RegistrarServerPort!UCI:asterisk/sip_service_provider,@i-1/port*/
static int get_ServicesVoiceServiceSIPNetwork_RegistrarServerPort(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "port", DEFAULT_SIP_PORT_STR);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_RegistrarServerPort(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, "port", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "port", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.RegistrarServerTransport!UCI:asterisk/sip_service_provider,@i-1/transport*/
static int get_ServicesVoiceServiceSIPNetwork_RegistrarServerTransport(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "transport", value);
	if (*value && **value) {
		for (char *ch = *value; *ch != '\0'; ch++)
			*ch = toupper(*ch);
	} else {
		*value = dmstrdup("UDP");
	}
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_RegistrarServerTransport(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, -1, RegistrarServerTransport, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			for (char *ch = value; *ch != '\0'; ch++)
				*ch = tolower(*ch);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "transport", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "transport", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.UserAgentDomain!UCI:asterisk/sip_service_provider,@i-1/domain*/
static int get_ServicesVoiceServiceSIPNetwork_UserAgentDomain(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "domain", value);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_UserAgentDomain(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;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "domain", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "domain", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.OutboundProxy!UCI:asterisk/sip_service_provider,@i-1/outbound_proxy*/
static int get_ServicesVoiceServiceSIPNetwork_OutboundProxy(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "outbound_proxy", value);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_OutboundProxy(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;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "outbound_proxy", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "outbound_proxy", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.OutboundProxyPort!UCI:asterisk/sip_service_provider,@i-1/outbound_proxy_port*/
static int get_ServicesVoiceServiceSIPNetwork_OutboundProxyPort(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "outbound_proxy_port", DEFAULT_SIP_PORT_STR);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_OutboundProxyPort(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, "outbound_proxy_port", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "outbound_proxy_port", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.STUNServer!UCI:asterisk/sip_advanced,sip_options/stun_server*/
static int get_ServicesVoiceServiceSIPNetwork_STUNServer(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_option_value_string("asterisk", "sip_options", "stun_server", value);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_STUNServer(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;
			break;
		case VALUESET:
			dmuci_set_value("asterisk", "sip_options", "stun_server", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.RegistrationPeriod!UCI:asterisk/sip_service_provider,@i-1/defaultexpiry*/
static int get_ServicesVoiceServiceSIPNetwork_RegistrationPeriod(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char buf[32];
	char *expiry = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "defaultexpiry", DEFAULT_SIP_REGISTER_EXPIRY_STR);
	snprintf(buf, sizeof(buf), "%d", (int)strtol(expiry, NULL, 10) - 10);
	*value = dmstrdup(buf);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_RegistrationPeriod(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;
			break;
		case VALUESET:
			{
				char buf[32];
				snprintf(buf, sizeof(buf), "%d", (int)strtol(value, NULL, 10) + 10);
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "defaultexpiry", buf);
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "defaultexpiry", buf);
			}
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.Realm!UCI:asterisk/sip_advanced,sip_options/realm*/
static int get_ServicesVoiceServiceSIPNetwork_Realm(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_option_value_string("asterisk", "sip_options", "realm", value);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_Realm(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, -1, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value("asterisk", "sip_options", "realm", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.RegisterExpires!UCI:asterisk/sip_service_provider,@i-1/defaultexpiry*/
static int get_ServicesVoiceServiceSIPNetwork_RegisterExpires(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "defaultexpiry", DEFAULT_SIP_REGISTER_EXPIRY_STR);
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_RegisterExpires(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;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "defaultexpiry", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "defaultexpiry", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.DSCPMark!UCI:asterisk/sip_advanced,sip_options/tos_sip*/
static int get_ServicesVoiceServiceSIPNetwork_DSCPMark(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_option_value_fallback_def("asterisk", "sip_options", "tos_sip", "0");
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_DSCPMark(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","63"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value("asterisk", "sip_options", "tos_sip", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.CodecList!UCI:asterisk/sip_service_provider,@i-1/codecs*/
static int get_ServicesVoiceServiceSIPNetwork_CodecList(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *codecs = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "codecs", &codecs);
	if (DM_STRLEN(codecs)) {
		char buf[256] = {0};
		char *token, *saveptr;
		int len = 0;

		buf[0] = 0;

		for (token = strtok_r(codecs, ", ", &saveptr); token; token = strtok_r(NULL, ", ", &saveptr)) {
			const char *codec = get_codec_name(token);
			if (codec && len < sizeof(buf)) {
				int res = snprintf(buf + len, sizeof(buf) - len, "%s%s", len == 0 ? "" : ",", codec);
				if (res <= 0) {
					BBF_DEBUG("buf might be too small\n");
					return FAULT_9002;
				}
				len += res;
			}
		}

		if (buf[0] != '\0')
			*value = dmstrdup(buf);
	}
	return 0;
}

static int set_ServicesVoiceServiceSIPNetwork_CodecList(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *codec_list = NULL, *token = NULL, *saveptr = NULL, *uci_name = NULL;
	int res = 0;

	switch (action)	{
		case VALUECHECK:
			if (!value)
				return FAULT_9007;
			else if (*value) {
				codec_list = dmstrdup(value);
				for (token = strtok_r(codec_list, ", ", &saveptr); token; token = strtok_r(NULL, ", ", &saveptr)) {
					if (!get_codec_uci_name(token)) {
						res = FAULT_9007;
						break;
					}
				}
			}
			break;
		case VALUESET:
			if (value) {
				// Empty the existing code list first
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "codecs", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "codecs", "");

				if (*value) {
					codec_list = dmstrdup(value);
					for (token = strtok_r(codec_list, ", ", &saveptr); token;
						 token = strtok_r(NULL, ", ", &saveptr)) {
						uci_name = (char *)get_codec_uci_name(token);
						if (uci_name) {
							dmuci_add_list_value_by_section(((struct dm_data *)data)->config_section, "codecs", uci_name);
							dmuci_add_list_value_by_section(((struct dm_data *)data)->dmmap_section, "codecs", uci_name);
						}
					}
				}
			}
			break;
	}

	if (codec_list)
		dmfree(codec_list);
	return res;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.FQDNServer.Enable!UCI:asterisk/sip_advanced,sip_options/srv_lookup*/
static int get_ServicesVoiceServiceSIPNetworkFQDNServer_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_option_value_string("asterisk", "sip_options", "srv_lookup", value);
	*value = (DM_LSTRCMP(*value, "yes") == 0) ? dmstrdup("1") : dmstrdup("0");
	return 0;
}

static int set_ServicesVoiceServiceSIPNetworkFQDNServer_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;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value("asterisk", "sip_options", "srv_lookup", b ? "yes" : "no");
			break;
	}
	return 0;
}

static int get_ServicesVoiceServiceSIPNetworkFQDNServer_Origin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("Static");
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.FQDNServer.Domain!UCI:asterisk/sip_service_provider,@i-1/domain*/
static int get_ServicesVoiceServiceSIPNetworkFQDNServer_Domain(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_server_address((struct dm_data *)data, "domain", value);
}

static int set_ServicesVoiceServiceSIPNetworkFQDNServer_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, 256, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			set_server_address((struct dm_data *)data, "domain", value);
			break;
	}
	return 0;
}

/*#Device.Services.VoiceService.{i}.SIP.Network.{i}.FQDNServer.Port!UCI:asterisk/sip_service_provider,@i-1/domain*/
static int get_ServicesVoiceServiceSIPNetworkFQDNServer_Port(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_server_port((struct dm_data *)data, "domain", value);
}

static int set_ServicesVoiceServiceSIPNetworkFQDNServer_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;
			break;
		case VALUESET:
			set_server_port((struct dm_data *)data, "domain", value);
			break;
	}
	return 0;
}

static int get_ServicesVoiceServiceSIPNetwork_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "networkalias", instance, value);
}

static int set_ServicesVoiceServiceSIPNetwork_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	return bbf_set_alias(ctx, ((struct dm_data *)data)->dmmap_section, "networkalias", instance, value);
}

/*Set Device.Services.VoiceService.{i}.SIP.Network.{i}.VoIPPofile!UCI:asterisk/sip_service_provider,@i-1/voipprofile*/
static int set_ServicesVoiceServiceSIPNetwork_VoIPProfile(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.Services.VoiceService.1.VoIPProfile.", NULL};
	struct dm_reference reference = {0};

	bbfdm_get_reference_linker(ctx, value, &reference);

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

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

			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "voipprofile", reference.value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "voipprofile", reference.value);
			break;
	}
	return 0;
}

/*Get Device.Services.VoiceService.{i}.SIP.Network.{i}.VoIPPofile!UCI:asterisk/sip_service_provider,@i-1/voipprofile*/
static int get_ServicesVoiceServiceSIPNetwork_VoIPProfile(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *linker = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "voipprofile", &linker);

	return _bbfdm_get_references(ctx, "Device.Services.VoiceService.1.VoIPProfile.", "Alias", linker, value);
}

/*Get Device.Services.VoiceService.{i}.SIP.Network.{i}.FQDNServer. Alias*/
static int get_ServicesVoiceServiceSIPNetworkFQDNServer_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
    return get_Alias_value_by_name(refparam, ctx, data, instance, value, "FQDNServer", "FQDNServer_inst");
}

/*Set Device.Services.VoiceService.{i}.SIP.Network.{i}.FQDNServer. Alias*/
static int set_ServicesVoiceServiceSIPNetworkFQDNServer_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
    return set_Alias_value_by_name(refparam, ctx, data, instance, value, action, "FQDNServer", "FQDNServer_inst");
}

/*Get Device.Services.VoiceService.{i}.SIP.Client.{i}. Alias*/
static int get_ServicesVoiceServiceSIPClient_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "clientalias", instance, value);
}

/*Set Device.Services.VoiceService.{i}.SIP.Client.{i}. Alias*/
static int set_ServicesVoiceServiceSIPClient_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	return bbf_set_alias(ctx, ((struct dm_data *)data)->dmmap_section, "clientalias", instance, value);
}

/**********************************************************************************************************************************
*                                            OBJ & PARAM DEFINITION
***********************************************************************************************************************************/
/* *** Device.Services.VoiceService.{i}.SIP. *** */
DMOBJ tServicesVoiceServiceSIPObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys*/
{"Client", &DMWRITE, addObjServicesVoiceServiceSIPClient, delObjServicesVoiceServiceSIPClient, NULL, browseServicesVoiceServiceSIPClientInst, NULL, NULL, tServicesVoiceServiceSIPClientObj, tServicesVoiceServiceSIPClientParams, NULL, BBFDM_BOTH, NULL},
{"Network", &DMWRITE, addObjServicesVoiceServiceSIPNetwork, delObjServicesVoiceServiceSIPNetwork, NULL, browseServicesVoiceServiceSIPNetworkInst, NULL, NULL, tServicesVoiceServiceSIPNetworkObj, tServicesVoiceServiceSIPNetworkParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tServicesVoiceServiceSIPParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type*/
{"NetworkNumberOfEntries", &DMREAD, DMT_UNINT, get_ServicesVoiceServiceSIP_NetworkNumberOfEntries, NULL, BBFDM_BOTH},
{"ClientNumberOfEntries", &DMREAD, DMT_UNINT, get_ServicesVoiceServiceSIP_ClientNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.Services.VoiceService.{i}.SIP.Client.{i}. *** */
DMOBJ tServicesVoiceServiceSIPClientObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys*/
// {"Contact", &DMWRITE, addObjServicesVoiceServiceSIPClientContact, delObjServicesVoiceServiceSIPClientContact, NULL, browseServicesVoiceServiceSIPClientContactInst, NULL, NULL, NULL, tServicesVoiceServiceSIPClientContactParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tServicesVoiceServiceSIPClientParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type*/
{"Enable", &DMWRITE, DMT_BOOL, get_ServicesVoiceServiceSIPClient_Enable, set_ServicesVoiceServiceSIPClient_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_ServicesVoiceServiceSIPClient_Status, NULL, BBFDM_BOTH},
{"Origin", &DMREAD, DMT_STRING, get_ServicesVoiceServiceSIPClient_Origin, NULL, BBFDM_BOTH},
{"AuthUserName", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPClient_AuthUserName, set_ServicesVoiceServiceSIPClient_AuthUserName, BBFDM_BOTH},
{"AuthPassword", &DMWRITE, DMT_STRING, get_empty, set_ServicesVoiceServiceSIPClient_AuthPassword, BBFDM_BOTH},
{"RegisterURI", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPClient_RegisterURI, set_ServicesVoiceServiceSIPClient_RegisterURI, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPClient_Alias, set_ServicesVoiceServiceSIPClient_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
{"Network", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPClient_Network, set_ServicesVoiceServiceSIPClient_Network, BBFDM_BOTH, DM_FLAG_REFERENCE},
{0}
};

/* *** Device.Services.VoiceService.{i}.SIP.Network.{i}. *** */
DMOBJ tServicesVoiceServiceSIPNetworkObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys*/
// Lowerlayer only support one FQDNServer, because of the limitation, this object defined as read only
{"FQDNServer", &DMREAD, NULL, NULL, NULL, browseServicesVoiceServiceSIPNetworkFQDNServerInst, NULL, NULL, NULL, tServicesVoiceServiceSIPNetworkFQDNServerParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tServicesVoiceServiceSIPNetworkParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type*/
{"Enable", &DMWRITE, DMT_BOOL, get_ServicesVoiceServiceSIPNetwork_Enable, set_ServicesVoiceServiceSIPNetwork_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_Status, NULL, BBFDM_BOTH},
{"ProxyServer", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_ProxyServer, set_ServicesVoiceServiceSIPNetwork_ProxyServer, BBFDM_BOTH},
{"ProxyServerPort", &DMWRITE, DMT_UNINT, get_ServicesVoiceServiceSIPNetwork_ProxyServerPort, set_ServicesVoiceServiceSIPNetwork_ProxyServerPort, BBFDM_BOTH},
{"ProxyServerTransport", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_Transport, set_ServicesVoiceServiceSIPNetwork_Transport, BBFDM_BOTH},
{"RegistrarServer", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_RegistrarServer, set_ServicesVoiceServiceSIPNetwork_RegistrarServer, BBFDM_BOTH},
{"RegistrarServerPort", &DMWRITE, DMT_UNINT, get_ServicesVoiceServiceSIPNetwork_RegistrarServerPort, set_ServicesVoiceServiceSIPNetwork_RegistrarServerPort, BBFDM_BOTH},
{"RegistrarServerTransport", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_RegistrarServerTransport, set_ServicesVoiceServiceSIPNetwork_RegistrarServerTransport, BBFDM_BOTH},
{"UserAgentDomain", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_UserAgentDomain, set_ServicesVoiceServiceSIPNetwork_UserAgentDomain, BBFDM_BOTH},
{"OutboundProxy", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_OutboundProxy, set_ServicesVoiceServiceSIPNetwork_OutboundProxy, BBFDM_BOTH},
{"OutboundProxyPort", &DMWRITE, DMT_UNINT, get_ServicesVoiceServiceSIPNetwork_OutboundProxyPort, set_ServicesVoiceServiceSIPNetwork_OutboundProxyPort, BBFDM_BOTH},
{"UserAgentTransport", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_Transport, set_ServicesVoiceServiceSIPNetwork_Transport, BBFDM_BOTH},
{"STUNServer", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_STUNServer, set_ServicesVoiceServiceSIPNetwork_STUNServer, BBFDM_BOTH},
{"RegistrationPeriod", &DMWRITE, DMT_UNINT, get_ServicesVoiceServiceSIPNetwork_RegistrationPeriod, set_ServicesVoiceServiceSIPNetwork_RegistrationPeriod, BBFDM_BOTH},
{"Realm", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_Realm, set_ServicesVoiceServiceSIPNetwork_Realm, BBFDM_BOTH},
{"RegisterExpires", &DMWRITE, DMT_UNINT, get_ServicesVoiceServiceSIPNetwork_RegisterExpires, set_ServicesVoiceServiceSIPNetwork_RegisterExpires, BBFDM_BOTH},
{"DSCPMark", &DMWRITE, DMT_UNINT, get_ServicesVoiceServiceSIPNetwork_DSCPMark, set_ServicesVoiceServiceSIPNetwork_DSCPMark, BBFDM_BOTH},
{"CodecList", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_CodecList, set_ServicesVoiceServiceSIPNetwork_CodecList, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_Alias, set_ServicesVoiceServiceSIPNetwork_Alias, BBFDM_BOTH, DM_FLAG_LINKER},
{"VoIPProfile", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetwork_VoIPProfile, set_ServicesVoiceServiceSIPNetwork_VoIPProfile, BBFDM_BOTH, DM_FLAG_REFERENCE},
{0}
};

/* *** Device.Services.VoiceService.{i}.SIP.Network.{i}.FQDNServer. *** */
DMLEAF tServicesVoiceServiceSIPNetworkFQDNServerParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type*/
{"Enable", &DMWRITE, DMT_BOOL, get_ServicesVoiceServiceSIPNetworkFQDNServer_Enable, set_ServicesVoiceServiceSIPNetworkFQDNServer_Enable, BBFDM_BOTH},
{"Origin", &DMREAD, DMT_STRING, get_ServicesVoiceServiceSIPNetworkFQDNServer_Origin, NULL, BBFDM_BOTH},
{"Domain", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetworkFQDNServer_Domain, set_ServicesVoiceServiceSIPNetworkFQDNServer_Domain, BBFDM_BOTH},
{"Port", &DMWRITE, DMT_UNINT, get_ServicesVoiceServiceSIPNetworkFQDNServer_Port, set_ServicesVoiceServiceSIPNetworkFQDNServer_Port, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_ServicesVoiceServiceSIPNetworkFQDNServer_Alias, set_ServicesVoiceServiceSIPNetworkFQDNServer_Alias, BBFDM_BOTH},
{0}
};
