/*
 * Copyright (C) 2019 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 MOHAMED Kallel <mohamed.kallel@pivasoftware.com>
 *	  Author Imen Bhiri <imen.bhiri@pivasoftware.com>
 *	  Author Feten Besbes <feten.besbes@pivasoftware.com>
 *	  Author Omar Kallel <omar.kallel@pivasoftware.com>
 *	  Author Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
 *
 */

#include "dmmem.h"
#include "dmcommon.h"
#include "dmbbf.h"

static char *get_parameter_notification(struct dmctx *ctx, char *param);
static int remove_parameter_notification(char *param);
static int set_parameter_notification(struct dmctx *ctx, char *param,char *value);
static int dm_browse(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *entryobj, void *data, char *instance);
static int get_value_obj(DMOBJECT_ARGS);
static int get_value_param(DMPARAM_ARGS);
static int mobj_get_value_in_param(DMOBJECT_ARGS);
static int mparam_get_value_in_param(DMPARAM_ARGS);
static int mparam_get_name(DMPARAM_ARGS);
static int mobj_get_name(DMOBJECT_ARGS);
static int mparam_get_name_in_param(DMPARAM_ARGS);
static int mobj_get_name_in_param(DMOBJECT_ARGS);
static int mparam_get_name_in_obj(DMPARAM_ARGS);
static int mobj_get_name_in_obj(DMOBJECT_ARGS);
static int mobj_get_schema_name(DMOBJECT_ARGS);
static int mparam_get_schema_name(DMPARAM_ARGS);
static int mobj_get_instances_in_obj(DMOBJECT_ARGS);
static int mparam_get_instances_in_obj(DMPARAM_ARGS);
static int mparam_add_object(DMPARAM_ARGS);
static int mobj_add_object(DMOBJECT_ARGS);
static int delete_object_obj(DMOBJECT_ARGS);
static int delete_object_param(DMPARAM_ARGS);
static int mobj_set_value(DMOBJECT_ARGS);
static int mparam_set_value(DMPARAM_ARGS);
static int mobj_get_notification_in_param(DMOBJECT_ARGS);
static int mobj_get_notification(DMOBJECT_ARGS);
static int mparam_get_notification(DMPARAM_ARGS);
static int mparam_get_notification_in_param(DMPARAM_ARGS);
static int mparam_set_notification_in_obj(DMPARAM_ARGS);
static int mobj_set_notification_in_param(DMOBJECT_ARGS);
static int mparam_set_notification_in_param(DMPARAM_ARGS);
static int mobj_set_notification_in_obj(DMOBJECT_ARGS);
static int dm_browse_schema(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *entryobj, void *data, char *instance);
#ifdef BBF_TR064
static int mparam_upnp_get_instances(DMPARAM_ARGS);
static int mobj_upnp_get_instances(DMOBJECT_ARGS);
static int mparam_upnp_structured_get_value_in_param(DMPARAM_ARGS);
static int mparam_upnp_get_supportedparams(DMPARAM_ARGS);
static int mparam_upnp_set_attributes(DMPARAM_ARGS);
static int mobj_upnp_set_attributes(DMOBJECT_ARGS);
static int mobj_upnp_get_supportedparams(DMOBJECT_ARGS);
static int mparam_upnp_get_attributes(DMPARAM_ARGS);
static int mobj_upnp_get_attributes(DMOBJECT_ARGS);
static int upnp_get_value_obj(DMOBJECT_ARGS);
static int upnp_get_value_param(DMPARAM_ARGS);
static int mobj_upnp_get_value_in_param(DMOBJECT_ARGS);
static int mparam_upnp_get_value_in_param(DMPARAM_ARGS);
static int mobj_upnp_set_value(DMOBJECT_ARGS);
static int mparam_upnp_set_value(DMPARAM_ARGS);
static int upnp_delete_instance_param(DMPARAM_ARGS);
static int upnp_delete_instance_obj(DMOBJECT_ARGS);
static int mparam_upnp_add_instance(DMPARAM_ARGS);
static int mobj_upnp_add_instance(DMOBJECT_ARGS);
static int mparam_upnp_get_acldata(DMPARAM_ARGS);
static int mobj_upnp_get_acldata(DMOBJECT_ARGS);
static int mparam_upnp_get_instance_numbers(DMPARAM_ARGS);
static int mobj_upnp_get_instance_numbers(DMOBJECT_ARGS);
static int enabled_tracked_param_check_obj(DMOBJECT_ARGS);
static int enabled_tracked_param_check_param(DMPARAM_ARGS);
#endif
static int enabled_notify_check_obj(DMOBJECT_ARGS);
static int enabled_notify_check_param(DMPARAM_ARGS);
static int get_linker_check_obj(DMOBJECT_ARGS);
static int get_linker_check_param(DMPARAM_ARGS);
static int get_linker_value_check_obj(DMOBJECT_ARGS);
static int get_linker_value_check_param(DMPARAM_ARGS);

#ifdef BBF_TR064
LIST_HEAD(list_upnp_enabled_onevent);
LIST_HEAD(list_upnp_enabled_onalarm);
LIST_HEAD(list_upnp_enabled_version);
LIST_HEAD(list_upnp_changed_onevent);
LIST_HEAD(list_upnp_changed_onalarm);
LIST_HEAD(list_upnp_changed_version);
#endif

LIST_HEAD(list_enabled_notify);

int ip_version = 4;
char dm_delim = DMDELIM_CWMP;
char dmroot[64] = "Device";
int bbfdatamodel_type = BBFDM_BOTH;

struct notification notifications[] = {
	[0] = {"0", "disabled"},
	[1] = {"1", "passive"},
	[2] = {"2", "active"},
	[3] = {"3", "passive_lw"},
	[4] = {"4", "passive_passive_lw"},
	[5] = {"5", "active_lw"},
	[6] = {"6", "passive_active_lw"}
};

struct dm_parameter forced_notifications_parameters[] = {
		{.name = "Device.DeviceInfo.SoftwareVersion", .notification = "2"},
		{.name = "Device.DeviceInfo.ProvisioningCode", .notification = "2"},
		{.name = "Device.ManagementServer.ConnectionRequestURL", .notification = "2"},
		{.name = "Device.ManagementServer.ConnReqJabberID", .notification = "2"},
		{.name = "Device.SoftwareModules.ExecutionUnit.*.Status", .notification = "2"}
};

struct dm_acl dm_acl[] = {
	[0] = {DM_PUBLIC_LIST, "public_list"},
	[1] = {DM_PUBLIC_READ, "public_read"},
	[2] = {DM_PUBLIC_WRITE, "public_write"},
	[3] = {DM_BASIC_LIST, "basic_list"},
	[4] = {DM_BASIC_READ, "basic_read"},
	[5] = {DM_BASIC_WRITE, "basic_write"},
	[6] = {DM_XXXADMIN_LIST, "xxxadmin_list"},
	[7] = {DM_XXXADMIN_READ, "xxxadmin_read"},
	[8] = {DM_XXXADMIN_WRITE, "xxxadmin_write"},
};

char *DMT_TYPE[] = {
[DMT_STRING] = "xsd:string",
[DMT_UNINT] = "xsd:unsignedInt",
[DMT_INT] = "xsd:int",
[DMT_UNLONG] = "xsd:unsignedLong",
[DMT_LONG] = "xsd:long",
[DMT_BOOL] = "xsd:boolean",
[DMT_TIME] = "xsd:dateTime",
[DMT_HEXBIN] = "xsd:hexBinary",
[DMT_BASE64] = "xsd:base64",
};

#ifdef BBF_TR064
unsigned int UPNP_DMT_TYPE[] = {
[DMT_STRING] = NODE_DATA_ATTRIBUTE_TYPESTRING,
[DMT_UNINT] = NODE_DATA_ATTRIBUTE_TYPEINT,
[DMT_INT] = NODE_DATA_ATTRIBUTE_TYPEINT,
[DMT_LONG] = NODE_DATA_ATTRIBUTE_TYPELONG,
[DMT_BOOL] = NODE_DATA_ATTRIBUTE_TYPEBOOL,
[DMT_TIME] = NODE_DATA_ATTRIBUTE_TYPEDATETIME,
[DMT_HEXBIN] = NODE_DATA_ATTRIBUTE_TYPEBIN,
};
#endif

struct dm_permession_s DMREAD = {"0", NULL};
struct dm_permession_s DMWRITE = {"1", NULL};

static int plugin_obj_match(DMOBJECT_ARGS)
{
	if (node->matched)
		return 0;
	if (!dmctx->inparam_isparam && strstr(node->current_object, dmctx->in_param) == node->current_object) {
		node->matched++;
		dmctx->findparam = 1;
		return 0;
	}
	if (strstr(dmctx->in_param, node->current_object) == dmctx->in_param) {
		return 0;
	}
	return FAULT_9005;
}

static int plugin_leaf_match(DMOBJECT_ARGS)
{
	char *str;
	if (node->matched)
		return 0;
	if (!dmctx->inparam_isparam)
		return FAULT_9005;
	str = dmctx->in_param + strlen(node->current_object);
	if (!strchr(str, dm_delim))
		return 0;
	return FAULT_9005;
}

static int plugin_leaf_onlyobj_match(DMOBJECT_ARGS)
{
	return FAULT_9005;
}

static int plugin_obj_nextlevel_match(DMOBJECT_ARGS)
{
	if (node->matched > 1)
		return FAULT_9005;
	if (node->matched) {
		node->matched++;
		return 0;
	}
	if (!dmctx->inparam_isparam && strstr(node->current_object, dmctx->in_param) == node->current_object) {
		node->matched++;
		dmctx->findparam = 1;
		return 0;
	}
	if (strstr(dmctx->in_param, node->current_object) == dmctx->in_param) {
		return 0;
	}
	return FAULT_9005;
}

static int plugin_leaf_nextlevel_match(DMOBJECT_ARGS)
{
	char *str;
	if (node->matched > 1)
		return FAULT_9005;
	if (node->matched)
		return 0;
	if (!dmctx->inparam_isparam)
		return FAULT_9005;
	str = dmctx->in_param + strlen(node->current_object);
	if (!strchr(str, dm_delim))
		return 0;
	return FAULT_9005;
}

static int bbfdatamodel_matches(const enum bbfdm_type_enum type)
{
	return bbfdatamodel_type == BBFDM_BOTH || type == BBFDM_BOTH || bbfdatamodel_type == type;
}

static bool check_dependency(const char *conf_obj)
{
	/* Available cases */
	/* one file => "file:/etc/config/network" */
	/* multiple files => "file:/etc/config/network,/lib/netifd/proto/dhcp.sh" */
	/* one ubus => "ubus:router.network" (with method : "ubus:router.network->hosts") */
	/* multiple ubus => "ubus:router.system->info,dsl->status,wifi" */
	/* common (files and ubus) => "file:/etc/config/network,/etc/config/dhcp;ubus:router.system,dsl->status" */

	char *pch, *spch;

	char *conf_list = dmstrdup(conf_obj);
	for (pch = strtok_r(conf_list, ";", &spch); pch != NULL; pch = strtok_r(NULL, ";", &spch)) {
		char *conf_type = strchr(pch, ':');
		if (!conf_type)
			return false;

		char *conf_name = dmstrdup(conf_type + 1);
		*conf_type = '\0';

		char *token, *saveptr;
		for (token = strtok_r(conf_name, ",", &saveptr); token != NULL; token = strtok_r(NULL, ",", &saveptr)) {
			if ((!strcmp(pch, "file") && !file_exists(token)) || (!strcmp(pch, "ubus") && !dmubus_object_method_exists(token)))
				return false;
		}
	}

	return true;
}

static int dm_browse_leaf(struct dmctx *dmctx, DMNODE *parent_node, DMLEAF *leaf, void *data, char *instance)
{
	int err = 0;
	if (!leaf)
		return 0;

	for (; leaf->parameter; leaf++) {
		if (!bbfdatamodel_matches(leaf->bbfdm_type))
			continue;
		err = dmctx->method_param(dmctx, parent_node, leaf->parameter, leaf->permission, leaf->type, leaf->getvalue, leaf->setvalue, data, instance);
		if (dmctx->stop)
			return err;
	}
	return err;
}

static void dm_browse_entry(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *entryobj, void *data, char *instance, char *parent_obj, int *err)
{
	DMNODE node = {0};

	node.obj = entryobj;
	node.parent = parent_node;
	node.instance_level = parent_node->instance_level;
	node.matched = parent_node->matched;
	dmasprintf(&(node.current_object), "%s%s%c", parent_obj, entryobj->obj, dm_delim);

	if (!bbfdatamodel_matches(entryobj->bbfdm_type))
		return;

	if (entryobj->checkdep && (check_dependency(entryobj->checkdep) == false))
		return;

	if (dmctx->checkobj) {
		*err = dmctx->checkobj(dmctx, &node, entryobj->permission, entryobj->addobj, entryobj->delobj, entryobj->get_linker, data, instance);
		if (*err)
			return;
	}

	*err = dmctx->method_obj(dmctx, &node, entryobj->permission, entryobj->addobj, entryobj->delobj, entryobj->get_linker, data, instance);
	if (dmctx->stop)
		return;

	if (entryobj->browseinstobj) {
		if (dmctx->instance_wildchar) {
			dm_link_inst_obj(dmctx, &node, data, dmctx->instance_wildchar);
			return;
		}
		else {
			entryobj->browseinstobj(dmctx, &node, data, instance);
			*err = dmctx->faultcode;
			return;
		}
	}

	if (entryobj->leaf) {
		if (dmctx->checkleaf) {
			*err = dmctx->checkleaf(dmctx, &node, entryobj->permission, entryobj->addobj, entryobj->delobj, entryobj->get_linker, data, instance);
			if (!*err) {
				*err = dm_browse_leaf(dmctx, &node, entryobj->leaf, data, instance);
				if (dmctx->stop)
					return;
			}
		} else {
			*err = dm_browse_leaf(dmctx, &node, entryobj->leaf, data, instance);
			if (dmctx->stop)
				return;
		}
	}

	if (entryobj->nextobj || entryobj->nextdynamicobj) {
		*err = dm_browse(dmctx, &node, entryobj->nextobj, data, instance);
	}
}

int dm_browse(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *entryobj, void *data, char *instance)
{
	DMOBJ *jentryobj;
	struct dm_dynamic_obj *next_dyn_array;
	int i, j, err = 0;

	char *parent_obj = parent_node->current_object;

	if (entryobj) {
		for (; entryobj->obj; entryobj++) {
			dm_browse_entry(dmctx, parent_node, entryobj, data, instance, parent_obj, &err);
			if (dmctx->stop)
				return err;
		}
	}

	if (parent_node->obj) {
		if (parent_node->obj->nextdynamicobj) {
			for (i = 0; i < __INDX_DYNAMIC_MAX; i++) {
				next_dyn_array = parent_node->obj->nextdynamicobj + i;
				if (next_dyn_array->nextobj) {
					for (j = 0; next_dyn_array->nextobj[j]; j++) {
						jentryobj = next_dyn_array->nextobj[j];
						for (; (jentryobj && jentryobj->obj); jentryobj++) {
							dm_browse_entry(dmctx, parent_node, jentryobj, data, instance, parent_obj, &err);
							if (dmctx->stop)
								return err;
						}
					}
				}
			}
		}
	}

	return err;
}

static void dm_browse_schema_entry(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *entryobj, void *data, char *instance, char *parent_obj, int *err)
{
	DMNODE node = {0};

	node.obj = entryobj;
	node.parent = parent_node;
	node.instance_level = parent_node->instance_level;
	node.matched = parent_node->matched;

	if (!bbfdatamodel_matches(entryobj->bbfdm_type))
		return;

	if (entryobj->checkdep && (check_dependency(entryobj->checkdep) == false))
		return;

	if (entryobj->browseinstobj) {
		dmasprintf(&(node.current_object), "%s%s%c{i}%c", parent_obj, entryobj->obj, dm_delim, dm_delim);
		if (dmctx->method_obj) {
			*err = dmctx->method_obj(dmctx, &node, entryobj->permission,
						 entryobj->addobj, entryobj->delobj,
						 entryobj->get_linker,
						 data, instance);
		}
	} else {
		dmasprintf(&(node.current_object), "%s%s%c", parent_obj, entryobj->obj, dm_delim);
	}

	if (entryobj->leaf)
		*err = dm_browse_leaf(dmctx, &node, entryobj->leaf, data, instance);

	if (entryobj->nextobj || entryobj->nextdynamicobj)
		*err = dm_browse_schema(dmctx, &node, entryobj->nextobj, data, instance);
}

static int dm_browse_schema(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *entryobj, void *data, char *instance)
{
	DMOBJ *jentryobj;
	struct dm_dynamic_obj *next_dyn_array;
	int i, j, err = 0;

	char *parent_obj = parent_node->current_object;

	if (entryobj) {
		for (; entryobj->obj; entryobj++) {
			dm_browse_schema_entry(dmctx, parent_node, entryobj, data, instance, parent_obj, &err);
			if (dmctx->stop)
				return err;
		}
	}

	if (parent_node->obj) {
		if (parent_node->obj->nextdynamicobj) {
			for (i = 0; i < __INDX_DYNAMIC_MAX; i++) {
				next_dyn_array = parent_node->obj->nextdynamicobj + i;
				if (next_dyn_array->nextobj) {
					for (j = 0; next_dyn_array->nextobj[j]; j++) {
						jentryobj = next_dyn_array->nextobj[j];
						for (; (jentryobj && jentryobj->obj); jentryobj++) {
							dm_browse_schema_entry(dmctx, parent_node, jentryobj, data, instance, parent_obj, &err);
							if (dmctx->stop)
								return err;
						}
					}
				}
			}
		}
	}
	return 0;
}

int dm_link_inst_obj(struct dmctx *dmctx, DMNODE *parent_node, void *data, char *instance)
{
	int err = 0;
	char *parent_obj;
	DMOBJ *prevobj = parent_node->obj;
	DMOBJ *nextobj = prevobj->nextobj;
	DMLEAF *nextleaf = prevobj->leaf;

	DMNODE node = {0};
	node.obj = prevobj;
	node.parent = parent_node;
	node.instance_level = parent_node->instance_level + 1;
	node.is_instanceobj = 1;
	node.matched = parent_node->matched;

	parent_obj = parent_node->current_object;
	if (instance == NULL)
		return -1;
	dmasprintf(&node.current_object, "%s%s%c", parent_obj, instance, dm_delim);
	if (dmctx->checkobj) {
		err = dmctx->checkobj(dmctx, &node, prevobj->permission, prevobj->addobj, prevobj->delobj, prevobj->get_linker, data, instance);
		if (err)
			return err;
	}
	err = dmctx->method_obj(dmctx, &node, prevobj->permission, prevobj->addobj, prevobj->delobj, prevobj->get_linker, data, instance);
	if (dmctx->stop)
		return err;
	if (nextleaf) {
		if (dmctx->checkleaf) {
			err = dmctx->checkleaf(dmctx, &node, prevobj->permission, prevobj->addobj, prevobj->delobj, prevobj->get_linker, data, instance);
			if (!err) {
				err = dm_browse_leaf(dmctx, &node, nextleaf, data, instance);
				if (dmctx->stop)
					return err;
			}
		} else {
			err = dm_browse_leaf(dmctx, &node, nextleaf, data, instance);
			if (dmctx->stop)
				return err;
		}
	}
	if (nextobj) {
		err = dm_browse(dmctx, &node, nextobj, data, instance);
		if (dmctx->stop)
			return err;
	}
	return err;
}

static int plugin_dynamic_obj_match(struct dmctx *dmctx, struct dmnode *node, char *entry_obj, char *full_obj)
{
	if (node->matched)
		return 0;

	if (!dmctx->inparam_isparam && strstr(node->current_object, full_obj) == node->current_object) {
		node->matched++;
		dmctx->findparam = 1;
		return 0;
	}

	if (strstr(full_obj, node->current_object) == full_obj)
		return 0;

	return FAULT_9005;
}

void dm_check_dynamic_obj(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *entryobj, char *full_obj, char *obj, DMOBJ **root_entry, int *obj_found)
{
	int err = 0;
	if (!entryobj)
		return;
	char *parent_obj = parent_node->current_object;
	for (; entryobj->obj; entryobj++) {
		DMNODE node = {0};
		node.obj = entryobj;
		node.parent = parent_node;
		node.instance_level = parent_node->instance_level;
		node.matched = parent_node->matched;
		dmasprintf(&(node.current_object), "%s%s%c", parent_obj, entryobj->obj, dm_delim);
		if (strcmp(node.current_object, obj) == 0) {
			*root_entry = entryobj;
			*obj_found = 1;
			return;
		}

		err = plugin_dynamic_obj_match(dmctx, &node, entryobj->obj, full_obj);
		if (err)
			continue;

		if (entryobj->nextobj)
			dm_check_dynamic_obj(dmctx, &node, entryobj->nextobj, full_obj, obj, root_entry, obj_found);
	}
}

int free_dm_browse_node_dynamic_object_tree(DMNODE *parent_node, DMOBJ *entryobj)
{
	if (!entryobj)
		return 0;

	for (; entryobj->obj; entryobj++) {
		if (entryobj->nextdynamicobj) {
			for (int i = 0; i < __INDX_DYNAMIC_MAX; i++) {
				struct dm_dynamic_obj *next_dyn_array = entryobj->nextdynamicobj + i;
				if (next_dyn_array->nextobj) FREE(next_dyn_array->nextobj);
			}
			FREE(entryobj->nextdynamicobj);
		}

		DMNODE node = {0};
		node.obj = entryobj;
		node.parent = parent_node;
		node.instance_level = parent_node->instance_level;
		node.matched = parent_node->matched;

		if (entryobj->nextobj)
			free_dm_browse_node_dynamic_object_tree(&node, entryobj->nextobj);
	}
	return 0;
}

static int rootcmp(char *inparam, char *rootobj)
{
	char buf[32];
	snprintf(buf, sizeof(buf), "%s%c", rootobj, dm_delim);
	int cmp = strcmp(inparam, buf);
	return cmp;
}

/***************************
 * update instance & alias
 ***************************/
char *handle_update_instance(int instance_ranck, struct dmctx *ctx, char **max_inst, char * (*up_instance)(int action, char **last_inst, char **max_inst, void *argv[]), int argc, ...)
{
	va_list arg;
	char *instance, *last_inst = NULL;
	int i = 0;
	unsigned int action, pos = instance_ranck - 1;
	void *argv[argc+1];

	va_start(arg, argc);
	for (i = 0; i < argc; i++) {
		argv[i] = va_arg(arg, void*);
	}
	argv[argc] = NULL;
	va_end(arg);
	if (ctx->amd_version >= AMD_4) {
		if(pos < ctx->nbrof_instance) {
			action = (ctx->alias_register & (1 << pos)) ? INSTANCE_UPDATE_ALIAS : INSTANCE_UPDATE_NUMBER;
		} else {
			action = (ctx->instance_mode == INSTANCE_MODE_ALIAS) ? INSTANCE_UPDATE_ALIAS : INSTANCE_UPDATE_NUMBER;
		}
	} else {
		action = INSTANCE_UPDATE_NUMBER;
	}

	instance = up_instance(action, &last_inst, max_inst, argv);
	if (last_inst)
		ctx->inst_buf[pos] = dmstrdup(last_inst);

	return instance;
}

char *update_instance(char *max_inst, int argc, ...)
{
	va_list arg;
	char *instance, *last_inst = NULL;
	int i = 0;
	void *argv[argc+1];

	va_start(arg, argc);
	for (i = 0; i < argc; i++) {
		argv[i] = va_arg(arg, void*);
	}
	argv[argc] = NULL;
	va_end(arg);

	instance = update_instance_alias(0, &last_inst, &max_inst, argv);

	return instance;
}

static int get_max_instance(char *dmmap_package, char *section_type, char *inst_opt, int (*check_browse)(struct uci_section *section, void *data), void *data)
{
	struct uci_section *s;
	char *inst;
	int max = 0;

	uci_path_foreach_sections(bbfdm, dmmap_package, section_type, s) {
		if (check_browse && check_browse(s, data) != 0)
			continue;

		dmuci_get_value_by_section_string(s, inst_opt, &inst);
		if (inst[0] == '\0')
			continue;

		max = max > atoi(inst) ? max : atoi(inst);
	}

	return max;
}

char *update_instance_alias(int action, char **last_inst, char **max_inst, void *argv[])
{
	char *instance, *alias;
	char buf[64] = {0};
	int max_instance = 0;

	struct uci_section *s = (struct uci_section *) argv[0];
	char *inst_opt = (char *) argv[1];
	char *alias_opt = (char *) argv[2];
	char *dmmap_package = (char *) argv[3];
	char *section_type = (char *) argv[4];
	int (*check_browse)(struct uci_section *section, void *data) = argv[5];
	void *data = (void *) argv[6];

	if (*max_inst == NULL)
		max_instance = get_max_instance(dmmap_package, section_type, inst_opt, check_browse, data);
	else
		max_instance = atoi(*max_inst);

	dmuci_get_value_by_section_string(s, inst_opt, &instance);
	if (instance[0] == '\0') {
		snprintf(buf, sizeof(buf), "%d", max_instance + 1);
		instance = dmuci_set_value_by_section(s, inst_opt, buf);
		*max_inst = instance;
	} else {
		dmasprintf(max_inst, "%d", max_instance);
	}
	*last_inst = instance;

	if (action == INSTANCE_MODE_ALIAS) {
		dmuci_get_value_by_section_string(s, alias_opt, &alias);
		if (alias[0] == '\0') {
			snprintf(buf, sizeof(buf), "cpe-%s", instance);
			alias = dmuci_set_value_by_section(s, alias_opt, buf);
		}
		snprintf(buf, sizeof(buf), "[%s]", alias);
		instance = dmstrdup(buf);
	}
	return instance;
}

char *update_instance_without_section(int action, char **last_inst, char **max_inst, void *argv[])
{
	char *instance, buf[64] = {0};
	int instnbr = (int)(long)argv[0];

	snprintf(buf, sizeof(buf), "%d", instnbr);
	instance = dmstrdup(buf);
	*last_inst = instance;

	if (action == INSTANCE_MODE_ALIAS) {
		snprintf(buf, sizeof(buf), "[cpe-%d]", instnbr);
		instance = dmstrdup(buf);
	}

	return instance;
}

char *get_last_instance_bbfdm(char *package, char *section, char *opt_inst)
{
	struct uci_section *s;
	char *inst = NULL, *last_inst = NULL;

	uci_path_foreach_sections(bbfdm, package, section, s) {
		inst = update_instance(last_inst, 4, s, opt_inst, package, section);
		if(last_inst)
			dmfree(last_inst);
		last_inst = dmstrdup(inst);
	}

	return inst;
}

char *get_last_instance_bbfdm_without_update(char *package, char *section, char *opt_inst)
{
	struct uci_section *s;
	char *inst = NULL, *last_inst = NULL;

	uci_path_foreach_sections(bbfdm, package, section, s) {
		dmuci_get_value_by_section_string(s, opt_inst, &inst);
		if(last_inst)
			dmfree(last_inst);
		last_inst = dmstrdup(inst);
	}
	return inst;
}

char *get_last_instance(char *package, char *section, char *opt_inst)
{
	struct uci_section *s;
	char *inst = NULL, *last_inst = NULL;

	if (strcmp(package, DMMAP) == 0) {
		uci_path_foreach_sections(bbfdm, "dmmap", section, s) {
			inst = update_instance(last_inst, 4, s, opt_inst, package, section);
			if(last_inst)
				dmfree(last_inst);
			last_inst = dmstrdup(inst);
		}
	} else {
		uci_foreach_sections(package, section, s) {
			inst = update_instance(inst, 4, s, opt_inst, package, section);
		}
	}
	return inst;
}

char *get_last_instance_lev2_bbfdm_dmmap_opt(char *dmmap_package, char *section, char *opt_inst, char *opt_check, char *value_check)
{
	struct uci_section *s;
	char *instance = NULL, *last_inst = NULL;
	struct browse_args browse_args = {0};

	browse_args.option = opt_check;
	browse_args.value = value_check;

	uci_path_foreach_option_eq(bbfdm, dmmap_package, section, opt_check, value_check, s) {
		instance = update_instance(last_inst, 6, s, opt_inst, dmmap_package, section, check_browse_section, (void *)&browse_args);
		if(last_inst)
			dmfree(last_inst);
		last_inst = dmstrdup(instance);
	}
	return instance;
}

char *get_last_instance_lev2_bbfdm(char *package, char *section, char* dmmap_package, char *opt_inst, char *opt_check, char *value_check)
{
	struct uci_section *s = NULL, *dmmap_section = NULL;
	char *instance = NULL, *last_inst = NULL;

	uci_foreach_option_cont(package, section, opt_check, value_check, s) {
		get_dmmap_section_of_config_section(dmmap_package, section, section_name(s), &dmmap_section);
		if (dmmap_section == NULL) {
			dmuci_add_section_bbfdm(dmmap_package, section, &dmmap_section);
			dmuci_set_value_by_section(dmmap_section, "section_name", section_name(s));
		}
		instance = update_instance(last_inst, 4, dmmap_section, opt_inst, dmmap_package, section);
		if(last_inst)
			dmfree(last_inst);
		last_inst = dmstrdup(instance);
	}
	return instance;
}

char *get_last_instance_lev2(char *package, char *section, char *opt_inst, char *opt_check, char *value_check)
{
	struct uci_section *s;
	char *instance = NULL, *last_inst = NULL;

	if (strcmp(package, DMMAP) == 0) {
		uci_path_foreach_option_cont(bbfdm, package, section, opt_check, value_check, s) {
			instance = update_instance(last_inst, 4, s, opt_inst, package, section);
			if(last_inst)
				dmfree(last_inst);
			last_inst = dmstrdup(instance);
		}
	} else {
		uci_foreach_option_cont(package, section, opt_check, value_check, s) {
			instance = update_instance(instance, 4, s, opt_inst, package, section);
		}
	}
	return instance;
}

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

void add_list_parameter(struct dmctx *ctx, char *param_name, char *param_data, char *param_type, char *param_version, unsigned int flags)
{
	struct dm_parameter *dm_parameter;
	struct list_head *ilist;
	list_for_each(ilist, &ctx->list_parameter) {
		dm_parameter = list_entry(ilist, struct dm_parameter, list);
		int cmp = strcmp(dm_parameter->name, param_name);
		if (cmp == 0) {
			return;
		} else if (cmp > 0) {
			break;
		}
	}
	dm_parameter = dmcalloc(1, sizeof(struct dm_parameter));
	_list_add(&dm_parameter->list, ilist->prev, ilist);
	dm_parameter->name = param_name;
	dm_parameter->data = param_data ? param_data : "";
	dm_parameter->type = param_type;
	dm_parameter->version = param_version;
	dm_parameter->flags = flags;
}

void api_del_list_parameter(struct dm_parameter *dm_parameter)
{
	list_del(&dm_parameter->list);
	dmfree(dm_parameter->name);
	dmfree(dm_parameter);
}

void free_all_list_parameter(struct dmctx *ctx)
{
	struct dm_parameter *dm_parameter;
	while (ctx->list_parameter.next != &ctx->list_parameter) {
		dm_parameter = list_entry(ctx->list_parameter.next, struct dm_parameter, list);
		api_del_list_parameter(dm_parameter);
	}
}

void add_set_list_tmp(struct dmctx *ctx, char *param, char *value, unsigned int flags)
{
	struct set_tmp *set_tmp;
	set_tmp = dmcalloc(1, sizeof(struct set_tmp));
	list_add_tail(&set_tmp->list, &ctx->set_list_tmp);
	set_tmp->name = dmstrdup(param);
	set_tmp->value = value ? dmstrdup(value) : NULL;
	set_tmp->flags = flags;
}

void del_set_list_tmp(struct set_tmp *set_tmp)
{
	list_del(&set_tmp->list);
	dmfree(set_tmp->name);
	dmfree(set_tmp->value);
	dmfree(set_tmp);
}

void free_all_set_list_tmp(struct dmctx *ctx)
{
	struct set_tmp *set_tmp;
	while (ctx->set_list_tmp.next != &ctx->set_list_tmp) {
		set_tmp = list_entry(ctx->set_list_tmp.next, struct set_tmp, list);
		del_set_list_tmp(set_tmp);
	}
}

void add_list_fault_param(struct dmctx *ctx, char *param, int fault)
{
	struct param_fault *param_fault;
	if (param == NULL) param = "";

	param_fault = dmcalloc(1, sizeof(struct param_fault));
	list_add_tail(&param_fault->list, &ctx->list_fault_param);
	param_fault->name = dmstrdup(param);
	param_fault->fault = fault;
}

void bbf_api_del_list_fault_param(struct param_fault *param_fault)
{
	list_del(&param_fault->list);
	dmfree(param_fault->name);
	dmfree(param_fault);
}

void free_all_list_fault_param(struct dmctx *ctx)
{
	struct param_fault *param_fault;
	while (ctx->list_fault_param.next != &ctx->list_fault_param) {
		param_fault = list_entry(ctx->list_fault_param.next, struct param_fault, list);
		bbf_api_del_list_fault_param(param_fault);
	}
}

int bbf_api_dm_update_file_enabled_notify(char *param, char *new_value)
{
	FILE *fp, *ftmp;
	char buf[512];
	char *parameter, *notification, *value, *type, *jval;

	fp = fopen(DM_ENABLED_NOTIFY, "r");
	if (fp == NULL)
		return 0;

	ftmp = fopen(DM_ENABLED_NOTIFY_TEMPORARY, "a");
	if (ftmp == NULL) {
		fclose(fp);
		return 0;
	}

	while (fgets(buf, 512, fp) != NULL) {
		int len = strlen(buf);
		if (len)
			buf[len-1] = '\0';
		bbf_api_dmjson_parse_init(buf);
		bbf_api_dmjson_get_string("parameter", &jval);
		if (jval == NULL || strlen(jval) == 0)
			continue;
		parameter = dmstrdup(jval?jval:"");
		bbf_api_dmjson_get_string("value", &jval);
		value = dmstrdup(jval?jval:"");
		bbf_api_dmjson_get_string("notification", &jval);
		notification = dmstrdup(jval?jval:"");
		bbf_api_dmjson_get_string("type", &jval);
		type = dmstrdup(jval?jval:"");
		bbf_api_dmjson_parse_fini();
		if (strcmp(parameter, param) == 0)
			dmjson_fprintf(ftmp, 4, DMJSON_ARGS{{"parameter", parameter}, {"notification", notification}, {"value", new_value}, {"type", type}});
		else
			dmjson_fprintf(ftmp, 4, DMJSON_ARGS{{"parameter", parameter}, {"notification", notification}, {"value", value}, {"type", type}});
	}
	fclose(fp);
	fclose(ftmp);

	return 0;
}

void dm_update_enabled_notify_byname(char *name, char *new_value)
{
	int iscopy;
	bbf_api_dm_update_file_enabled_notify(name, new_value);
	remove(DM_ENABLED_NOTIFY);
	iscopy = bbf_api_copy_temporary_file_to_original_file(DM_ENABLED_NOTIFY, DM_ENABLED_NOTIFY_TEMPORARY);
	if(iscopy)
		remove(DM_ENABLED_NOTIFY_TEMPORARY);

}

int update_param_instance_alias(struct dmctx *ctx, char *param, char **new_param)
{
	char *pch, *spch, *p;
	char buf[512];
	int i = 0, j = 0;
	char pat[2] = {0};

	char *dup = dmstrdup(param);
	*pat = dm_delim;
	p = buf;
	for (pch = strtok_r(dup, pat, &spch); pch != NULL; pch = strtok_r(NULL, pat, &spch)) {
		if (isdigit(pch[0])) {
			dmstrappendchr(p, dm_delim);
			dmstrappendstr(p, pch);
			i++;
		} else if (pch[0]== '[') {
			dmstrappendchr(p, dm_delim);
			dmstrappendstr(p, (ctx->inst_buf[i]) ? ctx->inst_buf[i] : "1");
			i++;
		} else {
			if (j > 0) {
				dmstrappendchr(p, dm_delim);
				dmstrappendstr(p, pch);
			}
			if (j == 0) {
				dmstrappendstr(p, pch);
				j++;
			}
		}
	}
	if (param[strlen(param) - 1] == dm_delim)
		dmstrappendchr(p, dm_delim);
	dmstrappendend(p);
	*new_param = dmstrdup(buf);
	dmfree(dup);
	return 0;
}

static char *get_parameter_notification(struct dmctx *ctx, char *param)
{
	int i, maxlen = 0, len;
	struct uci_list *list_notif;
	char *pch, *new_param;
	char *notification = "0";
	struct uci_element *e;

	update_param_instance_alias(ctx, param, &new_param);
	for (i = (ARRAY_SIZE(notifications) - 1); i >= 0; i--) {
		dmuci_get_option_value_list("cwmp", "@notifications[0]", notifications[i].type, &list_notif);
		if (list_notif) {
			uci_foreach_element(list_notif, e) {
				pch = e->name;
				if (strcmp(pch, new_param) == 0) {
					notification = notifications[i].value;
					return notification;
				}
				len = strlen(pch);
				if (pch[len-1] == dm_delim) {
					if (strstr(new_param, pch)) {
						if (len > maxlen )
						{
							notification = notifications[i].value;
							maxlen = len;
						}
					}
				}
			}
		}
	}
	dmfree(new_param);
	return notification;
}

static int remove_parameter_notification(char *param)
{
	int i;
	struct uci_list *list_notif;
	struct uci_element *e, *tmp;
	char *pch;
	for (i = (ARRAY_SIZE(notifications) - 1); i >= 0; i--) {
		if (param[strlen(param)-1] == dm_delim) {
			dmuci_get_option_value_list("cwmp", "@notifications[0]", notifications[i].type, &list_notif);
			if (list_notif) {
				uci_foreach_element_safe(list_notif, e, tmp) {
					pch = tmp->name;
					if (strstr(pch, param)) {
						dmuci_del_list_value("cwmp", "@notifications[0]", notifications[i].type, pch);
					}
				}
			}
		} else {
			dmuci_del_list_value("cwmp", "@notifications[0]", notifications[i].type, param);
		}
	}
	return 0;
}

static int set_parameter_notification(struct dmctx *ctx, char *param, char *value)
{
	char *tmp = NULL, *pch, *new_param;
	char *notification = NULL;
	struct uci_section *s;
	dmuci_get_section_type("cwmp", "@notifications[0]", &tmp);
	update_param_instance_alias(ctx, param, &new_param);
	if (!tmp || tmp[0] == '\0') {
		dmuci_add_section("cwmp", "notifications", &s);
	} else {
		remove_parameter_notification(new_param);
	}

	notification = get_parameter_notification(ctx, new_param);
	if (strcmp(notification, value) == 0) {
		goto end;
	}
	if (strcmp(value, "1") == 0) {
		dmuci_add_list_value("cwmp", "@notifications[0]", "passive", new_param);
	} else if (strcmp(value, "2") == 0) {
		dmuci_add_list_value("cwmp", "@notifications[0]", "active", new_param);
	} else if (strcmp(value, "3") == 0) {
		dmuci_add_list_value("cwmp", "@notifications[0]", "passive_lw", new_param);
	} else if (strcmp(value, "4") == 0) {
		dmuci_add_list_value("cwmp", "@notifications[0]", "passive_passive_lw", new_param);
	} else if (strcmp(value, "5") == 0) {
		dmuci_add_list_value("cwmp", "@notifications[0]", "active_lw", new_param);
	} else if (strcmp(value, "6") == 0) {
		dmuci_add_list_value("cwmp", "@notifications[0]", "passive_active_lw", new_param);
	} else if (strcmp(value, "0") == 0) {
		struct uci_list *list_notif;
		struct uci_element *e;
		int i, len;
		for (i = (ARRAY_SIZE(notifications) - 1); i >= 1; i--) {
			dmuci_get_option_value_list("cwmp", "@notifications[0]", notifications[i].type, &list_notif);
			if (list_notif) {
				uci_foreach_element(list_notif, e) {
					pch = e->name;
					len = strlen(pch);
					if (pch[len-1] == dm_delim && strstr(new_param, pch)) {
						dmuci_add_list_value("cwmp", "@notifications[0]", "disabled", new_param);
						goto end;
					}
				}
			}
		}

	} else {
		return -1;
	}
end:
	dmfree(new_param);
	return 0;
}

int string_to_bool(char *v, bool *b)
{
	if (v[0] == '1' && v[1] == '\0') {
		*b = true;
		return 0;
	}
	if (v[0] == '0' && v[1] == '\0') {
		*b = false;
		return 0;
	}
	if (strcasecmp(v, "true") == 0) {
		*b = true;
		return 0;
	}
	if (strcasecmp(v, "false") == 0) {
		*b = false;
		return 0;
	}
	*b = false;
	return -1;
}

static int is64digit(char c)
{
	if ((c >= '0' && c <= '9') ||
		(c >= 'a' && c <= 'z') ||
		(c >= 'A' && c <= 'Z') ||
		(c == '+' || c == '/' || c == '='))
		return 1;
	return 0;
}

static char *check_value_by_type(char *value, int type)
{
	int i = 0, len = strlen(value);
	char buf[len + 1];
	struct tm tm;

	strcpy(buf, value);
	buf[len] = 0;

	switch (type) {
		case DMT_UNINT:
		case DMT_UNLONG:
			while (buf[i] != 0) {
				if (isdigit(buf[i]) == 0)
					return "0";
				i++;
			}
			break;
		case DMT_INT:
		case DMT_LONG:
			if (buf[i] == '-')
				i++;
			while (buf[i] != 0) {
				if (isdigit(buf[i]) == 0)
					return "0";
				i++;
			}
			break;
		case DMT_BOOL:
			if (dm_validate_boolean(buf))
				return "0";
			break;
		case DMT_HEXBIN:
			while (buf[i] != 0) {
				if (isxdigit(buf[i]) == 0)
					return "";
				i++;
			}
			break;
		case DMT_BASE64:
			while (buf[i] != 0) {
				if (is64digit(buf[i]) == 0)
					return "0";
				i++;
			}
			break;
		case DMT_TIME:
			if (!strptime(buf, (len == 27) ? "%Y-%m-%dT%H:%M:%S." : "%Y-%m-%dT%H:%M:%SZ", &tm))
				return (len == 27) ? "0001-01-01T00:00:00.000000Z" : "0001-01-01T00:00:00Z";
			break;
		default:
			break;
	}
	return value;
}

static char *get_default_value_by_type(int type)
{
	switch (type) {
		case DMT_UNINT:
		case DMT_INT:
		case DMT_UNLONG:
		case DMT_LONG:
		case DMT_BOOL:
		case DMT_BASE64:
			return "0";
		case DMT_TIME:
			return "0001-01-01T00:00:00Z";
		default:
			return "";
	}
}

void dmentry_instance_lookup_inparam(struct dmctx *ctx)
{
	char *pch, *spch, *in_param;
	in_param = dmstrdup(ctx->in_param);
	int i = 0;
	char pat[2] = {0};
	*pat = dm_delim;
	for (pch = strtok_r(in_param, pat, &spch); pch != NULL; pch = strtok_r(NULL, pat, &spch)) {
		if (pch[0]== '[') {
			ctx->alias_register |= (1 << i);
			i++;
		} else if (isdigit(pch[0])) {
			i++;
		}
	}
	dmfree(in_param);
	ctx->nbrof_instance = i;
}

/* **********
 * get value 
 * **********/

int dm_entry_get_full_param_value(struct dmctx *dmctx)
{
	int err = 0;
	unsigned char findparam_check = 0;
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = {.current_object = ""};
	dmctx->inparam_isparam = 1;
	dmctx->findparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_match;
	dmctx->method_obj = mobj_get_value_in_param;
	dmctx->method_param = mparam_get_value_in_param;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (findparam_check && dmctx->findparam)
		return 0;
	else
		return err;
}

int dm_entry_get_value(struct dmctx *dmctx)
{
	int err = 0;
	unsigned char findparam_check = 0;
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = {.current_object = ""};

	if (dmctx->in_param[0] == '\0' || rootcmp(dmctx->in_param, root->obj) == 0) {
		dmctx->inparam_isparam = 0;
		dmctx->method_obj = get_value_obj;
		dmctx->method_param = get_value_param;
		dmctx->checkobj = NULL;
		dmctx->checkleaf = NULL;
		dmctx->findparam = 1;
		dmctx->stop = 0;
		findparam_check = 1;
	} else if (dmctx->in_param[strlen(dmctx->in_param) - 1] == dm_delim) {
		dmctx->inparam_isparam = 0;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_obj_match;
		dmctx->checkleaf = plugin_leaf_match;
		dmctx->method_obj = get_value_obj;
		dmctx->method_param = get_value_param;
		findparam_check = 1;
	} else {
		dmctx->inparam_isparam = 1;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_obj_match;
		dmctx->checkleaf = plugin_leaf_match;
		dmctx->method_obj = mobj_get_value_in_param;
		dmctx->method_param = mparam_get_value_in_param;
	}
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (findparam_check && dmctx->findparam)
		return 0;
	else
		return err;
}

static int get_value_obj(DMOBJECT_ARGS)
{
	return 0;
}

static int get_value_param(DMPARAM_ARGS)
{
	char *full_param;
	char *value = "";

	dmastrcat(&full_param, node->current_object, lastname);
	(get_cmd)(full_param, dmctx, data, instance, &value);

	value = (value && *value) ? check_value_by_type(value, type) : get_default_value_by_type(type);

	add_list_parameter(dmctx, full_param, value, DMT_TYPE[type], NULL, 0);
	return 0;
}

static int mobj_get_value_in_param(DMOBJECT_ARGS)
{
	return 0;
}
static int mparam_get_value_in_param(DMPARAM_ARGS)
{
	char *full_param;
	char *value = "";

	dmastrcat(&full_param, node->current_object, lastname);
	if (strcmp(dmctx->in_param, full_param) != 0) {
		dmfree(full_param);
		return FAULT_9005;
	}

	(get_cmd)(full_param, dmctx, data, instance, &value);

	value = (value && *value) ? check_value_by_type(value, type) : get_default_value_by_type(type);

	add_list_parameter(dmctx, full_param, value, DMT_TYPE[type], NULL, 0);
	dmctx->stop = true;
	return 0;
}

/* **********
 * get name 
 * **********/
int dm_entry_get_name(struct dmctx *ctx)
{
	DMOBJ *root = ctx->dm_entryobj;
	DMNODE node = {.current_object = ""};
	unsigned char findparam_check = 0;
	int err;
	if (ctx->nextlevel == 0	&& (ctx->in_param[0] == '\0' || rootcmp(ctx->in_param, root->obj) == 0)) {
		ctx->inparam_isparam = 0;
		ctx->findparam = 1;
		ctx->stop = 0;
		ctx->checkobj = NULL;
		ctx->checkleaf = NULL;
		ctx->method_obj = mobj_get_name;
		ctx->method_param = mparam_get_name;
	} else if (ctx->nextlevel && (ctx->in_param[0] == '\0')) {
		ctx->inparam_isparam = 0;
		ctx->findparam = 1;
		ctx->stop = 0;
		ctx->checkobj = plugin_obj_nextlevel_match;
		ctx->checkleaf = plugin_leaf_nextlevel_match;
		ctx->method_obj = mobj_get_name;
		ctx->method_param = mparam_get_name;
		ctx->in_param = root->obj;
		node.matched = 1;
		findparam_check = 1;
	} else if (*(ctx->in_param + strlen(ctx->in_param) - 1) == dm_delim) {
		ctx->inparam_isparam = 0;
		ctx->findparam = 0;
		ctx->stop = 0;
		ctx->method_obj = mobj_get_name_in_obj;
		ctx->method_param = mparam_get_name_in_obj;
		ctx->checkobj = (ctx->nextlevel) ? plugin_obj_nextlevel_match : plugin_obj_match;
		ctx->checkleaf = (ctx->nextlevel) ? plugin_leaf_nextlevel_match : plugin_leaf_match;
		findparam_check = 1;
	} else {
		ctx->inparam_isparam = 1;
		ctx->findparam = 0;
		ctx->stop = 0;
		ctx->checkobj = plugin_obj_match;
		ctx->checkleaf = plugin_leaf_match;
		ctx->method_obj = mobj_get_name_in_param;
		ctx->method_param = mparam_get_name_in_param;
	}
	err = dm_browse(ctx, &node, root, NULL, NULL);
	if (findparam_check && ctx->findparam)
		return 0;
	else
		return err;
}

static int mparam_get_name(DMPARAM_ARGS)
{
	char *refparam;
	char *perm = permission->val;
	dmastrcat(&refparam, node->current_object, lastname);
	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	add_list_parameter(dmctx, refparam, perm, DMT_TYPE[type], NULL, 0);
	return 0;
}

static int mobj_get_name(DMOBJECT_ARGS)
{
	char *refparam;
	char *perm = permission->val;
	refparam = node->current_object;
	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	add_list_parameter(dmctx, refparam, perm, "xsd:object", NULL, 0);
	return 0;
}

static int mparam_get_name_in_param(DMPARAM_ARGS)
{
	char *refparam;
	char *perm = permission->val;
	dmastrcat(&refparam, node->current_object, lastname);
	if (strcmp(refparam, dmctx->in_param) != 0) {
		dmfree(refparam);
		return FAULT_9005;
	}
	dmctx->stop = 1;
	if (dmctx->nextlevel == 1) {
		dmfree(refparam);
		return FAULT_9003;
	}
	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	add_list_parameter(dmctx, refparam, perm, DMT_TYPE[type], NULL, 0);
	return 0;
}

static int mobj_get_name_in_param(DMOBJECT_ARGS)
{
	return 0;
}

static int mparam_get_name_in_obj(DMPARAM_ARGS)
{
	char *refparam;
	char *perm = permission->val;

	dmastrcat(&refparam, node->current_object, lastname);

	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	add_list_parameter(dmctx, refparam, perm, DMT_TYPE[type], NULL, 0);
	return 0;
}

static int mobj_get_name_in_obj(DMOBJECT_ARGS)
{
	char *refparam;
	char *perm = permission->val;

	if (!node->matched) {
		return FAULT_9005;
	}

	if (dmctx->nextlevel && strcmp(node->current_object, dmctx->in_param) == 0)
		return 0;

	refparam = node->current_object;

	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	add_list_parameter(dmctx, refparam, perm, "xsd:object", NULL, 0);
	return 0;
}

/* **********
 * get schema
 * **********/
int dm_entry_get_schema(struct dmctx *ctx)
{
	DMOBJ *root = ctx->dm_entryobj;
	DMNODE node = {.current_object = ""};
	int err;

	ctx->inparam_isparam = 0;
	ctx->findparam = 0;
	ctx->stop = 0;
	ctx->checkobj = NULL;
	ctx->checkleaf = NULL;
	ctx->method_obj = mobj_get_schema_name;
	ctx->method_param = mparam_get_schema_name;
	err = dm_browse_schema(ctx, &node, root, NULL, NULL);
	return err;
}

static int mobj_get_schema_name(DMOBJECT_ARGS)
{
	char *perm = permission->val;
	char *refparam = node->current_object;
	const char **unique_keys = NULL;

	if (node->obj)
		unique_keys = node->obj->unique_keys;

	add_list_parameter(dmctx, refparam, perm, "xsd:object", (char *) unique_keys, 0);
	return 0;
}

static int mparam_get_schema_name(DMPARAM_ARGS)
{
	char *refparam;
	char *perm = permission->val;
	dmastrcat(&refparam, node->current_object, lastname);

	add_list_parameter(dmctx, refparam, perm, DMT_TYPE[type], NULL, 0);
	return 0;
}

/* **************
 * get_instances
 * **************/
int dm_entry_get_instances(struct dmctx *ctx)
{
	DMOBJ *root = ctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	char buf[4] = { dm_delim, 0 };
	size_t plen;
	int err;

	if (ctx->in_param[0] == 0)
		ctx->in_param = buf;

	plen = strlen(ctx->in_param);
	if (ctx->in_param[plen - 1] != dm_delim)
		return FAULT_9005;

	ctx->inparam_isparam = 0;
	ctx->findparam = 0;
	ctx->stop = 0;
	ctx->checkobj = (ctx->nextlevel) ? plugin_obj_nextlevel_match : plugin_obj_match;
	ctx->checkleaf = (ctx->nextlevel) ? plugin_leaf_nextlevel_match : plugin_leaf_match;
	ctx->method_obj = mobj_get_instances_in_obj;
	ctx->method_param = mparam_get_instances_in_obj;

	err = dm_browse(ctx, &node, root, NULL, NULL);
	if (ctx->findparam == 0)
		return err;

	return 0;
}

static int mobj_get_instances_in_obj(DMOBJECT_ARGS)
{
	if (node->matched && node->is_instanceobj) {
		char *name = dmstrdup(node->current_object);

		if (name) {
			name[strlen(name) - 1] = 0;
			add_list_parameter(dmctx, name, NULL, "xsd:object", NULL, 0);
		}
	}

	return 0;
}

static int mparam_get_instances_in_obj(DMPARAM_ARGS)
{
	return 0;
}

/* ********************
 * get notification
 * ********************/
int dm_entry_get_notification(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	unsigned char findparam_check = 0;
	int err;

	if (dmctx->in_param[0] == '\0'
		|| rootcmp(dmctx->in_param, root->obj) == 0) {
		dmctx->inparam_isparam = 0;
		dmctx->findparam = 1;
		dmctx->stop = 0;
		dmctx->checkobj = NULL;
		dmctx->checkleaf = NULL;
		dmctx->method_obj = mobj_get_notification;
		dmctx->method_param = mparam_get_notification;
		findparam_check = 1;
	} else if (*(dmctx->in_param + strlen(dmctx->in_param) - 1) == dm_delim) {
		dmctx->inparam_isparam = 0;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_obj_match;
		dmctx->checkleaf = plugin_leaf_match;
		dmctx->method_obj = mobj_get_notification;
		dmctx->method_param = mparam_get_notification;
		findparam_check = 1;
	} else {
		dmctx->inparam_isparam = 1;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_obj_match;
		dmctx->checkleaf = plugin_leaf_match;
		dmctx->method_obj = mobj_get_notification_in_param;
		dmctx->method_param = mparam_get_notification_in_param;
	}
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (findparam_check && dmctx->findparam)
		return 0;
	else
		return err;
}

static int mparam_get_notification(DMPARAM_ARGS)
{
	char *value;
	char *refparam;

	dmastrcat(&refparam, node->current_object, lastname);

	if (check_parameter_forced_notification(refparam) == NULL) {
		value = get_parameter_notification(dmctx, refparam);
	} else {
		value = check_parameter_forced_notification(refparam);
	}
	add_list_parameter(dmctx, refparam, value, DMT_TYPE[type], NULL, 0);
	return 0;
}

static int mobj_get_notification(DMOBJECT_ARGS)
{
	return 0;
}

static int mparam_get_notification_in_param(DMPARAM_ARGS)
{
	char *value = NULL;
	char *refparam;

	dmastrcat(&refparam, node->current_object, lastname);
	if (strcmp(refparam, dmctx->in_param) != 0) {
		dmfree(refparam);
		return FAULT_9005;
	}
	if (check_parameter_forced_notification(refparam) == NULL) {
		value = get_parameter_notification(dmctx, refparam);
	} else {
		value = check_parameter_forced_notification(refparam);
	}
	add_list_parameter(dmctx, refparam, value, DMT_TYPE[type], NULL, 0);
	dmctx->stop = 1;
	return 0;
}

static int mobj_get_notification_in_param(DMOBJECT_ARGS)
{
	return 0;
}

/* **************
 * add object 
 * **************/
int dm_entry_add_object(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;

	if (dmctx->in_param == NULL || dmctx->in_param[0] == '\0'
			|| (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim))
		return FAULT_9005;

	dmctx->inparam_isparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_onlyobj_match;
	dmctx->method_obj = mobj_add_object;
	dmctx->method_param = mparam_add_object;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
		return err;
	else
		return FAULT_9005;
}

static int mparam_add_object(DMPARAM_ARGS)
{
	return FAULT_9005;
}

static int mobj_add_object(DMOBJECT_ARGS)
{
	char *refparam = node->current_object;
	char *perm = permission->val;
	char *objinst;

	if (strcmp(refparam, dmctx->in_param) != 0)
		return FAULT_9005;

	dmctx->stop = 1;
	if (node->is_instanceobj)
		return FAULT_9005;
	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	if (perm[0] == '0' || addobj == NULL)
		return FAULT_9005;

	int fault = (addobj)(refparam, dmctx, data, &instance);
	if (fault)
		return fault;
	dmctx->addobj_instance = instance;
	dmasprintf(&objinst, "%s%s%c", node->current_object, instance, dm_delim);
	set_parameter_notification(dmctx, objinst, "0");
	dmfree(objinst);
	return 0;
}

/* **************
 * del object 
 * **************/
int dm_entry_delete_object(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;

	if (dmctx->in_param == NULL || dmctx->in_param[0] == '\0'
			|| (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim))
		return FAULT_9005;

	dmctx->inparam_isparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_onlyobj_match;
	dmctx->method_obj = delete_object_obj;
	dmctx->method_param = delete_object_param;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
		return err;
	else
		return FAULT_9005;
}

static int delete_object_obj(DMOBJECT_ARGS)
{
	char *refparam = node->current_object;
	char *perm = permission->val;
	unsigned char del_action = DEL_INST;
	if (strcmp(refparam, dmctx->in_param) != 0)
		return FAULT_9005;

	dmctx->stop = 1;

	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	if (perm[0] == '0' || delobj == NULL)
		return FAULT_9005;

	if (!node->is_instanceobj)
		del_action = DEL_ALL;
	int fault = (delobj)(refparam, dmctx, data, instance, del_action);
	return fault;
}

static int delete_object_param(DMPARAM_ARGS)
{
	return FAULT_9005;
}

/* **************
 * set value  
 * **************/
int dm_entry_set_value(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;

	if (dmctx->in_param == NULL || dmctx->in_param[0] == '\0'
			|| (*(dmctx->in_param + strlen(dmctx->in_param) - 1) == dm_delim))
		return FAULT_9005;

	dmctx->inparam_isparam = 1;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_match;
	dmctx->method_obj = mobj_set_value;
	dmctx->method_param = mparam_set_value;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
		return err;
	else
		return FAULT_9005;
}

static int mobj_set_value(DMOBJECT_ARGS)
{
	return FAULT_9005;
}

static int mparam_set_value(DMPARAM_ARGS)
{
	char *refparam, *perm;

	dmastrcat(&refparam, node->current_object, lastname);
	if (strcmp(refparam, dmctx->in_param) != 0) {
		dmfree(refparam);
		return FAULT_9005;
	}
	dmctx->stop = 1;

	if (dmctx->setaction == VALUECHECK) {
		perm = permission->val;
		if (permission->get_permission != NULL)
			perm = permission->get_permission(refparam, dmctx, data, instance);

		if (perm[0] == '0' || !set_cmd) {
			dmfree(refparam);
			return FAULT_9008;
		}
		int fault = (set_cmd)(refparam, dmctx, data, instance, dmctx->in_value, VALUECHECK);
		if (fault) {
			dmfree(refparam);
			return fault;
		}
		add_set_list_tmp(dmctx, dmctx->in_param, dmctx->in_value, 0);
	} else if (dmctx->setaction == VALUESET) {
		(set_cmd)(refparam, dmctx, data, instance, dmctx->in_value, VALUESET);
		dm_update_enabled_notify_byname(refparam, dmctx->in_value);
	}
	dmfree(refparam);
	return 0;
}

/* ****************
 * set notification  
 * ****************/
int dm_entry_set_notification(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;

	if (dmcommon_check_notification_value(dmctx->in_notification) < 0)
		return FAULT_9003;
	if (dmctx->in_param[0] == '\0' || rootcmp(dmctx->in_param, root->obj) == 0) {
		return FAULT_9009;
	} else if (*(dmctx->in_param + strlen(dmctx->in_param) - 1) == dm_delim) {
		dmctx->inparam_isparam = 0;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_obj_match;
		dmctx->checkleaf = plugin_leaf_match;
		dmctx->method_obj = mobj_set_notification_in_obj;
		dmctx->method_param = mparam_set_notification_in_obj;
	} else {
		dmctx->inparam_isparam = 1;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_obj_match;
		dmctx->checkleaf = plugin_leaf_match;
		dmctx->method_obj = mobj_set_notification_in_param;
		dmctx->method_param = mparam_set_notification_in_param;
	}
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
		return err;
	else
		return FAULT_9005;
}

/* SET Notification*/

static int mparam_set_notification_in_obj(DMPARAM_ARGS)
{
	return FAULT_9005;
}

static int mobj_set_notification_in_obj(DMOBJECT_ARGS)
{
	char *refparam = node->current_object;
	if (strcmp(refparam, dmctx->in_param) != 0) {
		return FAULT_9005;
	}
	dmctx->stop = 1;
	if (!dmctx->notification_change) {
		return 0;
	}
	if (dmctx->setaction == VALUECHECK) {
		if (check_parameter_forced_notification(refparam))
			return FAULT_9009;

		add_set_list_tmp(dmctx, dmctx->in_param, dmctx->in_notification, 0);
	} else if (dmctx->setaction == VALUESET) {
		set_parameter_notification(dmctx, dmctx->in_param, dmctx->in_notification);
		dmctx->end_session_flag |= BBF_END_SESSION_RELOAD;
	}
	return 0;
}

static int mparam_set_notification_in_param(DMPARAM_ARGS)
{
	char *refparam;

	dmastrcat(&refparam, node->current_object, lastname);
	if (strcmp(refparam, dmctx->in_param) != 0) {
		dmfree(refparam);
		return FAULT_9005;
	}

	dmctx->stop = 1;
	if (!dmctx->notification_change) {
		return 0;
	}
	if (dmctx->setaction == VALUECHECK) {
		if (check_parameter_forced_notification(refparam)) {
			dmfree(refparam);
			return FAULT_9009;
		}
		add_set_list_tmp(dmctx, dmctx->in_param, dmctx->in_notification, 0);
	} else if (dmctx->setaction == VALUESET) {
		set_parameter_notification(dmctx, dmctx->in_param, dmctx->in_notification);
		dmctx->end_session_flag |= BBF_END_SESSION_RELOAD;
	}
	dmfree(refparam);
	return 0;
}

static int mobj_set_notification_in_param(DMOBJECT_ARGS)
{
	return FAULT_9005;
}

/*********************
 * load enabled notify
 ********************/
int dm_entry_enabled_notify(struct dmctx *dmctx)
{
	int err;
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };

	dmctx->method_obj = enabled_notify_check_obj;
	dmctx->method_param = enabled_notify_check_param;
	dmctx->checkobj = NULL ;
	dmctx->checkleaf = NULL;
	remove(DM_ENABLED_NOTIFY);
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	return err;
}

char* check_parameter_forced_notification(char *parameter)
{
	int i;

	for (i=0; i<ARRAY_SIZE(forced_notifications_parameters); i++) {
		if (strcmp(forced_notifications_parameters[i].name, parameter) == 0 || check_instance_wildcard_parameter_by_regex(parameter, forced_notifications_parameters[i].name) == 0)
			return forced_notifications_parameters[i].notification;
	}
	return NULL;
}

static int enabled_notify_check_obj(DMOBJECT_ARGS)
{
	return FAULT_9005;
}

static int enabled_notify_check_param(DMPARAM_ARGS)
{
	char *refparam, *stype, *notif = NULL, *value = "";
	FILE *fp;

	dmastrcat(&refparam, node->current_object, lastname);
	if ((notif = check_parameter_forced_notification(refparam)) == NULL)
		notif = get_parameter_notification(dmctx, refparam);

	if (notif == NULL || notif[0] == '0') {
		dmfree(refparam);
		return 0;
	}
	(get_cmd)(refparam, dmctx, data, instance, &value);
	fp = fopen(DM_ENABLED_NOTIFY, "a");
	if (fp == NULL) {
		dmfree(refparam);
		return 0;
	}
	if (notif[0] == '1' || notif[0] == '2' || notif[0] == '4' || notif[0] == '6') {
		stype = DMT_TYPE[type];
		dmjson_fprintf(fp, 4, DMJSON_ARGS{{"parameter", refparam}, {"notification", notif}, {"value", value}, {"type", stype}});
	}
	fclose(fp);

	dmfree(refparam);
	return 0;
}

/******************
 * get linker param
 *****************/
int dm_entry_get_linker(struct dmctx *dmctx)
{
	int err;
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };

	dmctx->method_obj = get_linker_check_obj;
	dmctx->method_param = get_linker_check_param;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_onlyobj_match;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
		return err;
	else
		return FAULT_9005;
}

static int get_linker_check_obj(DMOBJECT_ARGS)
{
	char *link_val = "";

	if (!get_linker) {
		return  FAULT_9005;
	}
	if (node->obj->browseinstobj && !node->is_instanceobj) {
		return  FAULT_9005;
	}
	get_linker(node->current_object, dmctx, data, instance, &link_val);
	if (dmctx->linker[0] == '\0') {
		return  FAULT_9005;
	}
	if (link_val && link_val[0] != '\0' && strcmp(link_val, dmctx->linker) == 0) {
		if (node->current_object[strlen(node->current_object) - 1] == '.')
			node->current_object[strlen(node->current_object) - 1] = 0;
		dmctx->linker_param = dmstrdup(node->current_object);
		dmctx->stop = true;
		return 0;
	}
	return FAULT_9005;
}

static int get_linker_check_param(DMPARAM_ARGS)
{
	return FAULT_9005;
}

/******************
 * get linker value
 *****************/
int dm_entry_get_linker_value(struct dmctx *dmctx)
{
	int err ;
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };

	dmctx->method_obj = get_linker_value_check_obj;
	dmctx->method_param = get_linker_value_check_param;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_match;
	dmentry_instance_lookup_inparam(dmctx);
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
		return err;
	else
		return FAULT_9005;
}

static int get_linker_value_check_obj(DMOBJECT_ARGS)
{
	char *link_val;
	if (!get_linker)
		return FAULT_9005;

	if (strcmp(node->current_object, dmctx->in_param) == 0) {
		get_linker(node->current_object, dmctx, data, instance, &link_val);
		dmctx->linker = dmstrdup(link_val);
		dmctx->stop = true;
		return 0;
	}
	return FAULT_9005;
}

static int get_linker_value_check_param(DMPARAM_ARGS)
{
	return FAULT_9005;
}

#ifdef BBF_TR064
/* ******************
 * UPNP entries
 * ******************/

int upnp_map_bbf_fault(int bbf_fault)
{
	switch (bbf_fault) {
	case FAULT_9005:
		return FAULT_UPNP_703;
	case FAULT_9003:
		return FAULT_UPNP_701;
	case FAULT_9007:
		return FAULT_UPNP_705;
	case FAULT_9008:
		return FAULT_UPNP_706;
	}
	if (bbf_fault > __FAULT_UPNP_MAX)
		return FAULT_UPNP_701;
	return bbf_fault;
}

int plugin_upnp_structured_obj_match(DMOBJECT_ARGS)
{
	if (node->matched)
		return 0;
	if (!dmctx->inparam_isparam && strstructered(node->current_object, dmctx->in_param) == STRUCTERED_SAME) {
		node->matched++;
		dmctx->findparam = 1;
		return 0;
	}
	if (strstructered(dmctx->in_param, node->current_object) == STRUCTERED_PART) {
		return 0;
	}
	return FAULT_UPNP_703;
}

int plugin_upnp_leaf_match(DMOBJECT_ARGS)
{
	char *str;
	if (node->matched)
		return 0;
	if (!dmctx->inparam_isparam)
		return FAULT_UPNP_703;
	str = dmctx->in_param + strlen(node->current_object);
	if (!strchr(str, dm_delim))
		return 0;
	return FAULT_UPNP_703;
}

static int plugin_upnp_obj_depth_match(DMOBJECT_ARGS)
{
	if (node->matched) {
		node->matched++;
	}
	else if (strcmp(node->current_object, dmctx->in_param) == 0) {
		node->matched++;
		dmctx->findparam = 1;
	}
	else if (strstr(dmctx->in_param, node->current_object) == dmctx->in_param) {
		return 0;
	}
	if (dmctx->depth == 0 || dmctx->depth >= (node->matched - 1))
		return 0;
	return FAULT_UPNP_703;
}

int plugin_upnp_leaf_depth_match(DMOBJECT_ARGS)
{
	char *str;
	if (!node->matched) {
		if (!dmctx->inparam_isparam) {
			return FAULT_UPNP_703;
		}
		else {
			str = dmctx->in_param + strlen(node->current_object);
			if (strchr(str, dm_delim))
				return FAULT_UPNP_703;
		}
	}
	if (dmctx->depth == 0)
		return 0;
	if (dmctx->depth >= node->matched)
		return 0;
	return FAULT_UPNP_703;
}

static int plugin_upnp_skip_leafs(DMOBJECT_ARGS)
{
	return FAULT_UPNP_703;
}

static int upnp_get_parameter_onchange(struct dmctx *ctx, char *param, char *onchange)
{
	struct uci_list *list_onchange;
	char *pch;
	struct uci_element *e;
	dmuci_get_option_value_list(UPNP_CFG, "@notifications[0]", onchange, &list_onchange);
	if (list_onchange) {
		uci_foreach_element(list_onchange, e) {
			pch = e->name;
			if (strcmp(pch, param) == 0) {
				return 1;
			}
		}
	}
	return 0;
}

static int upnp_set_parameter_onchange(struct dmctx *ctx, char *param, char *onchange, unsigned int value)
{
	char *tmp;
	struct uci_section *s;

	dmuci_get_section_type(UPNP_CFG, "@notifications[0]", &tmp);
	if (!tmp || tmp[0] == '\0') {
		dmuci_add_section(UPNP_CFG, "notifications", &s);
	}

	dmuci_del_list_value(UPNP_CFG, "@notifications[0]",onchange, param);
	if (value) {
		dmuci_add_list_value(UPNP_CFG, "@notifications[0]", onchange, param);
	}
	return 0;
}

void add_list_upnp_param_track(struct dmctx *dmctx, struct list_head *pchead, char *param, char *key, char *value, unsigned int isobj)
{
	struct dm_upnp_enabled_track *dm_upnp_enabled_track;

	dm_upnp_enabled_track = calloc(1, sizeof(struct dm_upnp_enabled_track)); // Should be calloc and not dmcalloc
	list_add_tail(&dm_upnp_enabled_track->list, pchead);
	dm_upnp_enabled_track->name = strdup(param); // Should be strdup and not dmstrdup
	dm_upnp_enabled_track->value = value ? strdup(value) : strdup(""); // Should be strdup and not dmstrdup
	dm_upnp_enabled_track->key = key ? strdup(key) : NULL; // Should be strdup and not dmstrdup
	dm_upnp_enabled_track->isobj = isobj;
}

void del_list_upnp_param_track(struct dm_upnp_enabled_track *dm_upnp_enabled_track)
{
	list_del(&dm_upnp_enabled_track->list); // Should be free and not dmfree
	free(dm_upnp_enabled_track->name);
	free(dm_upnp_enabled_track->value);
	free(dm_upnp_enabled_track->key);
	free(dm_upnp_enabled_track);
}

void free_all_list_upnp_param_track(struct list_head *pchead)
{
	struct dm_upnp_enabled_track *dm_upnp_enabled_track;
	while (pchead->next != pchead) {
		dm_upnp_enabled_track = list_entry(pchead->next, struct dm_upnp_enabled_track, list);
		del_list_upnp_param_track(dm_upnp_enabled_track);
	}
}

int get_parameter_version(struct dmctx *ctx, char *param, char **version, struct uci_section **rs)
{
	struct uci_list *list_struc;
	struct uci_section *s = NULL;
	char *pch;
	struct uci_element *e;

	*version = NULL;
	*rs = NULL;

	uci_foreach_option_eq(UPNP_CFG, "parameter_version", "parameter", param, s) {
		break;
	}
	if (s != NULL) {
		*rs = s;
		dmuci_get_value_by_section_string(s, "version", version);
		if (**version == '\0')
			*version = "0";
		return 1;
	} else {
		dmuci_get_option_value_list(UPNP_CFG, "@parameter_version_structured[0]", "paramater", &list_struc);
		if (list_struc) {
			uci_foreach_element(list_struc, e) {
				pch = e->name;
				if (strstructered(pch, param) == STRUCTERED_SAME) {
					*version = "0";
					return 1;
				}
			}
		}
	}
	return 0;
}

unsigned int get_parameter_acl(struct dmctx *ctx, char *param)
{
	int i, maxlen = 0, len;
	struct uci_list *list_acl;
	char *pch;
	unsigned int acl = 0, f;
	struct uci_element *e;

	for (i = (ARRAY_SIZE(dm_acl) - 1); i >= 0; i--) {
		dmuci_get_option_value_list(UPNP_CFG, "@acl_factorized[0]", dm_acl[i].user_access, &list_acl);
		if (list_acl) {
			f = 0;
			maxlen = 0;
			uci_foreach_element(list_acl, e) {
				pch = e->name;
				if (strstructered(pch, param) == STRUCTERED_SAME) {
					f = dm_acl[i].flag;
					break;
				}
				len = strlen(pch);
				if (pch[len-1] == dm_delim) {
					if (strstructered(param, pch) == STRUCTERED_PART) {
						if (len > maxlen )
						{
							f = dm_acl[i].flag;
							maxlen = len;
						}
					}
				}
			}
			acl |= f;
		}
	}
	return acl;
}

unsigned int dm_upnp_check_acl_list(struct dmctx *dmctx, char *param)
{
	unsigned int flag;
	unsigned int mask = DM_LIST_MASK | DM_READ_MASK | DM_WRITE_MASK;
	flag = get_parameter_acl(dmctx, param);
	if (flag & mask & dmctx->user_mask)
		return flag;
	return 0;
}

unsigned int dm_upnp_check_acl_read(struct dmctx *dmctx, char *param)
{
	unsigned int flag;
	unsigned int mask = DM_READ_MASK | DM_WRITE_MASK;
	flag = get_parameter_acl(dmctx, param);
	if (flag & mask & dmctx->user_mask)
		return flag;
	return 0;
}

unsigned int dm_upnp_check_acl_write(struct dmctx *dmctx, char *param)
{
	unsigned int flag;
	unsigned int mask = DM_WRITE_MASK;
	flag = get_parameter_acl(dmctx, param);
	if (flag & mask & dmctx->user_mask)
		return flag;
	return 0;
}

int dm_entry_upnp_update_attribute_values_update(struct dmctx *dmctx)
{
	char *v, *tmp, buf[32];
	struct uci_section *s;
	int version;

	dmuci_get_option_value_string(UPNP_CFG, "@dm[0]", "attribute_values_version", &v);
	version = atoi(v);
	version++;

	dmuci_get_section_type(UPNP_CFG, "@dm[0]", &tmp);
	if (!tmp || tmp[0] == '\0') {
		dmuci_add_section(UPNP_CFG, "dm", &s);
	}
	snprintf(buf, sizeof(buf), "%d", version);
	dmuci_set_value(UPNP_CFG, "@dm[0]", "attribute_values_version", buf);
	snprintf(buf, sizeof(buf), "%ld", time(NULL));
	dmuci_set_value(UPNP_CFG, "@dm[0]", "attribute_values_epochtime", buf);

	return 0;
}

/* ******************
 * UPNP get instances
 * ******************/

int dm_entry_upnp_get_instances(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = {.current_object = ""};
	int err;
	char buf[4] = {0};
	buf[0] = dm_delim;

	if (*(dmctx->in_param) == '\0')
		dmctx->in_param = buf;

	if (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim)
		return FAULT_UPNP_701;

	dmctx->inparam_isparam = 0;
	dmctx->findparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_upnp_obj_depth_match;
	dmctx->checkleaf = plugin_upnp_skip_leafs;
	dmctx->method_obj = mobj_upnp_get_instances;
	dmctx->method_param = mparam_upnp_get_instances;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->findparam)
		return 0;
	else
		return (upnp_map_bbf_fault(err));
}

static int mparam_upnp_get_instances(DMPARAM_ARGS)
{
	return 0;
}

static int mobj_upnp_get_instances(DMOBJECT_ARGS)
{
	char *refparam;
	refparam = node->current_object;
	if (!node->is_instanceobj || !node->matched)
		return FAULT_UPNP_703;
	if (!dm_upnp_check_acl_read(dmctx, refparam)) {
		dmctx->findparam = 0;
		dmctx->stop = 1;
		return FAULT_UPNP_703;
	}
	add_list_parameter(dmctx, refparam, NULL, NULL, NULL, 0);
	return 0;
}

/* *****************************
 * UPNP get supported parameters
 * *****************************/

int dm_entry_upnp_get_supported_parameters(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = {.current_object = ""};
	int err;

	char buf[4] = {0};
	buf[0] = dm_delim;

	if (*(dmctx->in_param) == '\0')
		dmctx->in_param = buf;

	if (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim)
		dmctx->inparam_isparam = 1;
	else
		dmctx->inparam_isparam = 0;
	dmctx->instance_wildchar = "#";
	dmctx->findparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_upnp_obj_depth_match;
	dmctx->checkleaf = plugin_upnp_leaf_depth_match;
	dmctx->method_obj = mobj_upnp_get_supportedparams;
	dmctx->method_param = mparam_upnp_get_supportedparams;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->findparam)
		return 0;
	else
		return (upnp_map_bbf_fault(err));
}

static int mparam_upnp_get_supportedparams(DMPARAM_ARGS)
{
	char *refparam;
	dmastrcat(&refparam, node->current_object, lastname);
	if (dmctx->inparam_isparam) {
		if (strcmp(refparam, dmctx->in_param) == 0) {
			if (!dm_upnp_check_acl_list(dmctx, refparam)) {
				dmctx->findparam = 0;
				dmctx->stop = 1;
				return FAULT_UPNP_703;
			}
			dmctx->findparam = 1;
			add_list_parameter(dmctx, refparam, NULL, NULL, NULL, 0);
			return 0;
		}
	}
	else {
		if (!dm_upnp_check_acl_list(dmctx, refparam)) {
			dmctx->findparam = 0;
			dmctx->stop = 1;
			return FAULT_UPNP_703;
		}
		add_list_parameter(dmctx, refparam, NULL, NULL, NULL, 0);
		return 0;
	}
	return FAULT_UPNP_703;
}

static int mobj_upnp_get_supportedparams(DMOBJECT_ARGS)
{
	char *refparam;
	if (node->matched <= 1)
		return FAULT_UPNP_703;
	if (dmctx->depth == (node->matched - 1)) {
		if (node->obj->browseinstobj && !node->is_instanceobj)
			dmastrcat(&refparam, node->current_object, "#/");
		else
			refparam = node->current_object;
		if (!dm_upnp_check_acl_list(dmctx, refparam)) {
			dmctx->findparam = 0;
			dmctx->stop = 1;
			return FAULT_UPNP_703;
		}
		add_list_parameter(dmctx, refparam, NULL, NULL, NULL, 0);
		return 0;
	}
	return FAULT_UPNP_703;
}

/* ************************
 * UPNP get selected values
 * ************************/

int dm_entry_upnp_get_selected_values(struct dmctx *dmctx)
{
	int err = 0;
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = {.current_object = ""};

	if (dmctx->in_param[0] == '\0' || rootcmp(dmctx->in_param, root->obj) == 0) {
		dmctx->inparam_isparam = 0;
		dmctx->method_obj = get_value_obj;
		dmctx->method_param = get_value_param;
		dmctx->checkobj = NULL;
		dmctx->checkleaf = NULL;
		dmctx->findparam = 1;
		dmctx->stop = 0;
	} else if (dmctx->in_param[strlen(dmctx->in_param) - 1] == dm_delim) {
		dmctx->inparam_isparam = 0;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_upnp_structured_obj_match;
		dmctx->checkleaf = plugin_upnp_leaf_match;
		dmctx->method_obj = get_value_obj;
		dmctx->method_param = get_value_param;
	} else {
		dmctx->inparam_isparam = 1;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_upnp_structured_obj_match;
		dmctx->checkleaf = plugin_upnp_leaf_match;
		dmctx->method_obj = mobj_get_value_in_param;
		dmctx->method_param = mparam_upnp_structured_get_value_in_param;
	}
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->findparam)
		return 0;
	else
		return (upnp_map_bbf_fault(err));
}

static int mparam_upnp_structured_get_value_in_param(DMPARAM_ARGS)
{
	char *full_param;
	char *value = "";

	dmastrcat(&full_param, node->current_object, lastname);
	if (strstructered(dmctx->in_param, full_param) == STRUCTERED_NULL) {
		dmfree(full_param);
		return FAULT_UPNP_703;
	}
	if (!dm_upnp_check_acl_read(dmctx, full_param)) {
		dmctx->findparam = 0;
		dmctx->stop = 1;
		return FAULT_UPNP_703;
	}
	dmctx->findparam = 1;
	(get_cmd)(full_param, dmctx, data, instance, &value);
	add_list_parameter(dmctx, full_param, value, DMT_TYPE[type], NULL, 0);
	return 0;
}

/* ***************
 * UPNP get values
 * ***************/
int dm_entry_upnp_get_values(struct dmctx *dmctx)
{
	int err = 0;
	unsigned char findparam_check = 0;
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = {.current_object = ""};

	if (dmctx->in_param[0] == '\0' || rootcmp(dmctx->in_param, root->obj) == 0) {
		dmctx->inparam_isparam = 0;
		dmctx->method_obj = upnp_get_value_obj;
		dmctx->method_param = upnp_get_value_param;
		dmctx->checkobj = NULL;
		dmctx->checkleaf = NULL;
		dmctx->findparam = 1;
		dmctx->stop = 0;
		findparam_check = 1;
	} else if (dmctx->in_param[strlen(dmctx->in_param) - 1] == dm_delim) {
		dmctx->inparam_isparam = 0;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_obj_match;
		dmctx->checkleaf = plugin_leaf_match;
		dmctx->method_obj = upnp_get_value_obj;
		dmctx->method_param = upnp_get_value_param;
		findparam_check = 1;
	} else {
		dmctx->inparam_isparam = 1;
		dmctx->findparam = 0;
		dmctx->stop = 0;
		dmctx->checkobj = plugin_obj_match;
		dmctx->checkleaf = plugin_leaf_match;
		dmctx->method_obj = mobj_upnp_get_value_in_param;
		dmctx->method_param = mparam_upnp_get_value_in_param;
	}
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (findparam_check && dmctx->findparam)
		return 0;
	else
	return (upnp_map_bbf_fault(err));
}

static int upnp_get_value_obj(DMOBJECT_ARGS)
{
	return 0;
}

static int upnp_get_value_param(DMPARAM_ARGS)
{
	char *full_param;
	char *value = "";

	dmastrcat(&full_param, node->current_object, lastname);
	if (!dm_upnp_check_acl_read(dmctx, full_param)) {
		dmctx->findparam = 0;
		dmctx->stop = 1;
		return FAULT_UPNP_703;
	}
	(get_cmd)(full_param, dmctx, data, instance, &value);
	add_list_parameter(dmctx, full_param, value, NULL, NULL, 0);
	return 0;
}

static int mobj_upnp_get_value_in_param(DMOBJECT_ARGS)
{
	return 0;
}
static int mparam_upnp_get_value_in_param(DMPARAM_ARGS)
{
	char *full_param;
	char *value = "";

	dmastrcat(&full_param, node->current_object, lastname);
	if (strcmp(dmctx->in_param, full_param) != 0) {
		dmfree(full_param);
		return FAULT_UPNP_703;
	}

	if (!dm_upnp_check_acl_read(dmctx, full_param)) {
		dmctx->findparam = 0;
		dmctx->stop = 1;
		return FAULT_UPNP_703;
	}
	(get_cmd)(full_param, dmctx, data, instance, &value);
	add_list_parameter(dmctx, full_param, value, NULL, NULL, 0);
	dmctx->stop = 1;
	return 0;
}

/* ***************
 * UPNP set values
 * ***************/

int dm_entry_upnp_set_values(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;

	if (dmctx->in_param == NULL || dmctx->in_param[0] == '\0'
			|| (*(dmctx->in_param + strlen(dmctx->in_param) - 1) == dm_delim))
		return FAULT_UPNP_703;

	dmctx->inparam_isparam = 1;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_match;
	dmctx->method_obj = mobj_upnp_set_value;
	dmctx->method_param = mparam_upnp_set_value;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
		return (upnp_map_bbf_fault(err));
	else
		return FAULT_UPNP_703;
}

static int mobj_upnp_set_value(DMOBJECT_ARGS)
{
	return FAULT_UPNP_703;
}

static int mparam_upnp_set_value(DMPARAM_ARGS)
{
	char *refparam, *perm, *v = "";

	dmastrcat(&refparam, node->current_object, lastname);
	if (strcmp(refparam, dmctx->in_param) != 0) {
		dmfree(refparam);
		return FAULT_UPNP_703;
	}
	dmctx->stop = 1;

	if (dmctx->setaction == VALUECHECK) {
		perm = permission->val;
		if (permission->get_permission != NULL)
			perm = permission->get_permission(refparam, dmctx, data, instance);

		if (perm[0] == '0' || !set_cmd) {
			dmfree(refparam);
			return FAULT_UPNP_706;
		}
		if (!dm_upnp_check_acl_write(dmctx, refparam)) {
			dmctx->findparam = 0;
			return FAULT_UPNP_706;
		}
		int fault = (set_cmd)(refparam, dmctx, data, instance, dmctx->in_value, VALUECHECK);
		if (fault) {
			dmfree(refparam);
			return fault;
		}
		add_set_list_tmp(dmctx, dmctx->in_param, dmctx->in_value, 0);
	}
	else if (dmctx->setaction == VALUESET) {
		(set_cmd)(refparam, dmctx, data, instance, dmctx->in_value, VALUESET);
		(get_cmd)(refparam, dmctx, data, instance, &v);
		dm_update_enabled_notify_byname(refparam, v);
	}
	dmfree(refparam);
	return 0;
}

/* ********************
 * UPNP delete instance
 * *******************/

int dm_entry_upnp_delete_instance(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;

	if (dmctx->in_param == NULL || dmctx->in_param[0] == '\0'
			|| (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim))
		return FAULT_UPNP_703;

	dmctx->inparam_isparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_onlyobj_match;
	dmctx->method_obj = upnp_delete_instance_obj;
	dmctx->method_param = upnp_delete_instance_param;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
	return (upnp_map_bbf_fault(err));
	else
		return FAULT_UPNP_703;
}

static int upnp_delete_instance_obj(DMOBJECT_ARGS)
{
	char *refparam = node->current_object;
	char *perm = permission->val;
	unsigned char del_action = DEL_INST;
	if (strcmp(refparam, dmctx->in_param) != 0)
		return FAULT_UPNP_703;

	dmctx->stop = 1;

	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	if (perm[0] == '0' || delobj == NULL)
		return FAULT_UPNP_706;

	if (!dm_upnp_check_acl_write(dmctx, refparam)) {
		dmctx->findparam = 0;
		return FAULT_UPNP_706;
	}

	if (!node->is_instanceobj)
		del_action = DEL_ALL;
	int fault = (delobj)(refparam, dmctx, data, instance, del_action);
	return fault;
}

static int upnp_delete_instance_param(DMPARAM_ARGS)
{
	return FAULT_UPNP_703;
}

/* ********************
 * UPNP add instance
 * *******************/

int dm_entry_upnp_add_instance(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;

	if (dmctx->in_param == NULL || dmctx->in_param[0] == '\0'
			|| (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim))
		return FAULT_UPNP_703;

	dmctx->inparam_isparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_onlyobj_match;
	dmctx->method_obj = mobj_upnp_add_instance;
	dmctx->method_param = mparam_upnp_add_instance;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->stop)
	return (upnp_map_bbf_fault(err));
	else
		return FAULT_UPNP_703;
}

static int mparam_upnp_add_instance(DMPARAM_ARGS)
{
	return FAULT_UPNP_703;
}

static int mobj_upnp_add_instance(DMOBJECT_ARGS)
{
	char *refparam = node->current_object;
	char *perm = permission->val;
	char *objinst;

	if (strcmp(refparam, dmctx->in_param) != 0)
		return FAULT_UPNP_703;

	dmctx->stop = 1;
	if (node->is_instanceobj)
		return FAULT_UPNP_703;
	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	if (perm[0] == '0' || addobj == NULL)
		return FAULT_UPNP_706;

	if (!dm_upnp_check_acl_write(dmctx, refparam)) {
		dmctx->findparam = 0;
		return FAULT_UPNP_706;
	}

	int fault = (addobj)(refparam, dmctx, data, &instance);
	if (fault)
		return fault;
	dmctx->addobj_instance = instance;
	dmasprintf(&objinst, "%s%s%c", node->current_object, instance, dm_delim);
	set_parameter_notification(dmctx, objinst, "0");
	dmfree(objinst);
	return 0;
}

/* ********************
 * UPNP get attributes
 * ********************/
int dm_entry_upnp_get_attributes(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;
	char buf[4] = {0};
	buf[0] = dm_delim;

	if (*(dmctx->in_param) == '\0')
		dmctx->in_param = buf;

	if (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim)
		dmctx->inparam_isparam = 1;
	else
		dmctx->inparam_isparam = 0;

	dmctx->findparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_match;
	dmctx->method_obj = mobj_upnp_get_attributes;
	dmctx->method_param = mparam_upnp_get_attributes;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->findparam)
		return 0;
	else
		return (upnp_map_bbf_fault(err));
}

static int mparam_upnp_get_attributes(DMPARAM_ARGS)
{
	char *refparam;
	unsigned int flags = 0;
	char *perm = permission->val;
	char *version = NULL;
	struct uci_section *s = NULL;

	dmastrcat(&refparam, node->current_object, lastname);

	if (strcmp(refparam, dmctx->in_param) != 0)
			return FAULT_UPNP_703;

	if (!dm_upnp_check_acl_read(dmctx, refparam)) {
		dmctx->findparam = 0;
		dmctx->stop = 1;
		return FAULT_UPNP_703;
	}

	dmctx->stop = 1;

	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	perm = (*perm == '1') ? "readWrite" : "readOnly";

	if (upnp_get_parameter_onchange(dmctx, refparam, "alarmchange"))
		flags |= DM_PARAM_ALARAM_ON_CHANGE;
	if (upnp_get_parameter_onchange(dmctx, refparam, "eventchange"))
		flags |= DM_PARAM_EVENT_ON_CHANGE;
	get_parameter_version(dmctx, refparam, &version, &s);
	flags |= UPNP_DMT_TYPE[type];

	add_list_parameter(dmctx, refparam, perm, NULL, version, flags);
	return 0;
}

static int mobj_upnp_get_attributes(DMOBJECT_ARGS)
{
	char *refparam;
	unsigned int flags = 0;
	char *perm = permission->val;
	char *version = NULL;
	struct uci_section *s = NULL;

	refparam = node->current_object;

	if (strcmp(refparam, dmctx->in_param) != 0)
			return FAULT_UPNP_703;

	if (!dm_upnp_check_acl_read(dmctx, refparam)) {
		dmctx->findparam = 0;
		dmctx->stop = 1;
		return FAULT_UPNP_703;
	}

	dmctx->stop = 1;

	if (permission->get_permission != NULL)
		perm = permission->get_permission(refparam, dmctx, data, instance);

	perm = (*perm == '1') ? "readWrite" : "readOnly";

	if (!node->is_instanceobj && upnp_get_parameter_onchange(dmctx, refparam, "eventchange"))
		flags |= DM_PARAM_EVENT_ON_CHANGE;

	get_parameter_version(dmctx, refparam, &version, &s);

	if (node->is_instanceobj)
		flags |= (NODE_DATA_ATTRIBUTE_INSTANCE | NODE_DATA_ATTRIBUTE_TYPEPTR);
	else if (node->obj->browseinstobj)
		flags |= (NODE_DATA_ATTRIBUTE_MULTIINSTANCE | NODE_DATA_ATTRIBUTE_TYPEPTR);

	add_list_parameter(dmctx, refparam, perm, NULL, version, flags);

	return 0;
}

/* ********************
 * UPNP set attributes
 * ********************/
int dm_entry_upnp_set_attributes(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;
	char buf[4] = {0};
	buf[0] = dm_delim;

	if (*(dmctx->in_param) == '\0')
		dmctx->in_param = buf;

	if (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim)
		dmctx->inparam_isparam = 1;
	else
		dmctx->inparam_isparam = 0;

	dmctx->findparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_match;
	dmctx->method_obj = mobj_upnp_set_attributes;
	dmctx->method_param = mparam_upnp_set_attributes;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->findparam)
		return 0;
	else
		return (upnp_map_bbf_fault(err));
}

static int mparam_upnp_set_attributes(DMPARAM_ARGS)
{
	char *refparam;

	dmastrcat(&refparam, node->current_object, lastname);

	if (strcmp(refparam, dmctx->in_param) != 0)
			return FAULT_UPNP_703;

	dmctx->stop = 1;
	if (dmctx->setaction == VALUECHECK) {
		if (!dm_upnp_check_acl_write(dmctx, refparam)) {
			dmctx->findparam = 0;
			return FAULT_UPNP_706;
		}
		add_set_list_tmp(dmctx, dmctx->in_param, NULL, dmctx->dmparam_flags);
	}
	else {
		upnp_set_parameter_onchange(dmctx, refparam, "alarmchange", dmctx->dmparam_flags & DM_PARAM_ALARAM_ON_CHANGE);
		upnp_set_parameter_onchange(dmctx, refparam, "eventchange", dmctx->dmparam_flags & DM_PARAM_EVENT_ON_CHANGE);
		dm_entry_upnp_update_attribute_values_update(dmctx);
	}
	return 0;
}

static int mobj_upnp_set_attributes(DMOBJECT_ARGS)
{
	char *refparam;

	refparam = node->current_object;

	if (strcmp(refparam, dmctx->in_param) != 0)
			return FAULT_UPNP_703;

	dmctx->stop = 1;

	if (dmctx->setaction == VALUECHECK) {
		if (!dm_upnp_check_acl_write(dmctx, refparam)) {
			dmctx->findparam = 0;
			return FAULT_UPNP_706;
		}
		add_set_list_tmp(dmctx, dmctx->in_param, NULL, dmctx->dmparam_flags);
	}
	else {
		if (!node->is_instanceobj) {
			upnp_set_parameter_onchange(dmctx, refparam,  "eventchange", dmctx->dmparam_flags & DM_PARAM_EVENT_ON_CHANGE);
			dm_entry_upnp_update_attribute_values_update(dmctx);
		}
	}
	return 0;
}

/* *******************
 * UPNP get ACL data
 * ******************/

int dm_entry_upnp_get_acl_data(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };
	int err;
	char buf[4] = {0};
	buf[0] = dm_delim;

	if (*(dmctx->in_param) == '\0')
		dmctx->in_param = buf;

	if (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim)
		dmctx->inparam_isparam = 1;
	else
		dmctx->inparam_isparam = 0;

	if (strchr(dmctx->in_param, '#'))
		dmctx->instance_wildchar = "#";
	else
		dmctx->instance_wildchar = NULL;
	dmctx->findparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_obj_match;
	dmctx->checkleaf = plugin_leaf_match;
	dmctx->method_obj = mobj_upnp_get_acldata;
	dmctx->method_param = mparam_upnp_get_acldata;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->findparam)
		return 0;
	else
		return (upnp_map_bbf_fault(err));
}

static int mparam_upnp_get_acldata(DMPARAM_ARGS)
{
	char *refparam;
	unsigned int flags = 0;

	dmastrcat(&refparam, node->current_object, lastname);

	if (strcmp(refparam, dmctx->in_param) != 0)
			return FAULT_UPNP_703;

	flags = dm_upnp_check_acl_list(dmctx, refparam);
	if (!flags) {
		dmctx->findparam = 0;
		dmctx->stop = 1;
		return FAULT_UPNP_703;
	}

	dmctx->stop = 1;

	add_list_parameter(dmctx, refparam, NULL, NULL, NULL, flags);
	return 0;
}

static int mobj_upnp_get_acldata(DMOBJECT_ARGS)
{
	char *refparam;
	unsigned int flags = 0;

	refparam = node->current_object;

	if (strcmp(refparam, dmctx->in_param) != 0)
			return FAULT_UPNP_703;

	flags = dm_upnp_check_acl_list(dmctx, refparam);
	if (!flags) {
		dmctx->findparam = 0;
		dmctx->stop = 1;
		return FAULT_UPNP_703;
	}

	dmctx->stop = 1;
	flags |= DM_FACTORIZED;

	add_list_parameter(dmctx, refparam, NULL, NULL, NULL, flags);
	return 0;
}

/* **************************
 * UPNP get instance numbers
 * *************************/

char *dm_entry_get_all_instance_numbers(struct dmctx *pctx, char *param)
{
	static char instbuf[256];
	struct dmctx dmctx = {0};

	dm_ctx_init_sub(&dmctx, pctx->dm_type,  pctx->amd_version, pctx->instance_mode);
	dmctx.in_param = param;
	dmctx.depth = 1;
	dm_entry_upnp_get_instance_numbers(&dmctx);
	strcpy(instbuf, dmctx.all_instances);
	dm_ctx_clean_sub(&dmctx);
	return instbuf;
}

int dm_entry_upnp_get_instance_numbers(struct dmctx *dmctx)
{
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = {.current_object = ""};
	int err;
	char buf[4] = {0};
	buf[0] = dm_delim;

	if (*(dmctx->in_param) == '\0')
		dmctx->in_param = buf;

	if (*(dmctx->in_param + strlen(dmctx->in_param) - 1) != dm_delim)
		return FAULT_UPNP_701;

	dmctx->inparam_isparam = 0;
	dmctx->findparam = 0;
	dmctx->stop = 0;
	dmctx->checkobj = plugin_upnp_obj_depth_match;
	dmctx->checkleaf = plugin_upnp_skip_leafs;
	dmctx->method_obj = mobj_upnp_get_instance_numbers;
	dmctx->method_param = mparam_upnp_get_instance_numbers;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	if (dmctx->findparam)
		return 0;
	else
		return (upnp_map_bbf_fault(err));
}

static int mparam_upnp_get_instance_numbers(DMPARAM_ARGS)
{
	return 0;
}

static int mobj_upnp_get_instance_numbers(DMOBJECT_ARGS)
{
	char *refparam;
	refparam = node->current_object;
	if (!node->is_instanceobj || !node->matched)
		return FAULT_UPNP_703;
	if (*(dmctx->all_instances)) {
		strcat(dmctx->all_instances, ",");
	}
	strcat(dmctx->all_instances, instance);
	return 0;
}

/************************************
 * upnp load tracked parameter values
 ***********************************/
int dm_entry_upnp_tracked_parameters(struct dmctx *dmctx)
{
	int err;
	DMOBJ *root = dmctx->dm_entryobj;
	DMNODE node = { .current_object = "" };

	dmctx->method_obj = enabled_tracked_param_check_obj;
	dmctx->method_param = enabled_tracked_param_check_param;
	dmctx->checkobj = NULL ;
	dmctx->checkleaf = NULL;
	err = dm_browse(dmctx, &node, root, NULL, NULL);
	return err;
}

static int enabled_tracked_param_check_obj(DMOBJECT_ARGS)
{
	char *refparam;
	char *all_instances;
	char *version = "0";
	struct uci_section *s;
	int isevnt, isversion;

	refparam = node->current_object;
	if (!node->obj->browseinstobj || node->is_instanceobj)
		return FAULT_UPNP_703;

	isevnt = upnp_get_parameter_onchange(dmctx, refparam, "eventchange");
	isversion = get_parameter_version(dmctx, refparam, &version, &s);
	if (isevnt || isversion)
		all_instances = dm_entry_get_all_instance_numbers(dmctx, refparam);

	if (isevnt) {
		add_list_upnp_param_track(dmctx, &list_upnp_enabled_onevent, refparam, "1", all_instances, 1);
	}
	if (isversion) {
		add_list_upnp_param_track(dmctx, &list_upnp_enabled_version, refparam, (s ? section_name(s) : NULL), all_instances, 1);
	}
	return 0;
}

static int enabled_tracked_param_check_param(DMPARAM_ARGS)
{
	char *refparam;
	char *value = "";
	char *version = "0";
	struct uci_section *s;
	int isalrm, isevnt, isversion;

	dmastrcat(&refparam, node->current_object, lastname);
	isalrm = upnp_get_parameter_onchange(dmctx, refparam, "alarmchange");
	isevnt = upnp_get_parameter_onchange(dmctx, refparam, "eventchange");
	isversion = get_parameter_version(dmctx, refparam, &version, &s);

	if (isalrm || isevnt || isversion)
		(get_cmd)(refparam, dmctx, data, instance, &value);
	if (isalrm) {
		add_list_upnp_param_track(dmctx, &list_upnp_enabled_onalarm, refparam, "1", value, 0);
	}
	if (isevnt) {
		add_list_upnp_param_track(dmctx, &list_upnp_enabled_onevent, refparam, "1", value, 0);
	}
	if (isversion) {
		add_list_upnp_param_track(dmctx, &list_upnp_enabled_version, refparam, (s ? section_name(s) : NULL), value, 0);
	}

	dmfree(refparam);
	return 0;
}
#endif

/********************/
char *dm_print_path(char *fpath, ...)
{
	static char pathbuf[512] = "";
	va_list vl;

	va_start(vl, fpath);
	vsprintf(pathbuf, fpath, vl);
	va_end(vl);
	return pathbuf;
}
