/*
 * 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: 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 <curl/curl.h>

#include "dmcommon.h"


char *DiagnosticsState[] = {"None", "Requested", "Canceled", "Complete", "Error", NULL};

char *IPv4Address[] = {"^$", "^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$", NULL};
char *IPv6Address[] = {"^$", "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$", NULL};
char *IPAddress[] = {"^$", "^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$", "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$", NULL};
char *MACAddress[] = {"^$", "^([0-9A-Fa-f][0-9A-Fa-f]:){5}([0-9A-Fa-f][0-9A-Fa-f])$", NULL};
char *IPPrefix[] = {"^$", "^/(3[0-2]|[012]?[0-9])$", "^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])/(3[0-2]|[012]?[0-9])$", "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/(12[0-8]|1[0-1][0-9]|[0-9]?[0-9])$", NULL};
char *IPv4Prefix[] = {"^$", "^/(3[0-2]|[012]?[0-9])$", "^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])/(3[0-2]|[012]?[0-9])$", NULL};
char *IPv6Prefix[] = {"^$", "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/(12[0-8]|1[0-1][0-9]|[0-9]?[0-9])$", NULL};

pid_t get_pid(const char *pname)
{
	DIR* dir;
	struct dirent* ent;
	char* endptr;
	char buf[512];

	if (!(dir = opendir("/proc"))) {
		return -1;
	}
	while((ent = readdir(dir)) != NULL) {
		long lpid = strtol(ent->d_name, &endptr, 10);
		if (*endptr != '\0') {
			continue;
		}
		snprintf(buf, sizeof(buf), "/proc/%ld/cmdline", lpid);
		FILE* fp = fopen(buf, "r");
		if (fp) {
			if (fgets(buf, sizeof(buf), fp) != NULL) {
				char* first = strtok(buf, " ");
				if (DM_STRSTR(first, pname)) {
					fclose(fp);
					closedir(dir);
					return (pid_t)lpid;
				}
			}
			fclose(fp);
		}
	}
	closedir(dir);
	return -1;
}

int compare_strings(const void *a, const void *b)
{
	return DM_STRCMP(*(const char **)a, *(const char **)b);
}

char *get_uptime(void)
{
    FILE *fp = fopen(UPTIME, "r");
    char *uptime = "0";

	if (fp != NULL) {
		char *pch = NULL, *spch = NULL, buf[64] = {0};

		if (fgets(buf, 64, fp) != NULL) {
			pch = strtok_r(buf, ".", &spch);
			uptime = (pch) ? dmstrdup(pch) : "0";
		}
		fclose(fp);
	}

    return uptime;
}

int check_file(char *path)
{
	glob_t globbuf;
	if(glob(path, 0, NULL, &globbuf) == 0) {
		globfree(&globbuf);
		return 1;
	}
	return 0;
}

char *cidr2netmask(int bits)
{
	static char buf[INET_ADDRSTRLEN];

	uint32_t mask = (bits >= 32) ? 0xFFFFFFFFUL : (0xFFFFFFFFUL << (32 - bits));
	mask = htonl(mask);

	struct in_addr ip_addr;
	ip_addr.s_addr = mask;

	if (inet_ntop(AF_INET, &ip_addr, buf, INET_ADDRSTRLEN) == NULL) {
		// Error converting binary to presentation format
		return "";
	}

	return buf;
}

int netmask2cidr(const char *netmask)
{
	struct in_addr addr;
	int bits = 0;

	if (!netmask || inet_aton(netmask, &addr) == 0) {
		// Invalid netmask format
		return -1;
	}

	uint32_t mask = ntohl(addr.s_addr);

	while (mask & 0x80000000) {
		bits++;
		mask <<= 1;
	}

	return bits;
}


bool is_strword_in_optionvalue(char *optionvalue, char *str)
{
	char *s = optionvalue;
	while ((s = DM_STRSTR(s, str))) {
		int len = DM_STRLEN(str); //should be inside while, optimization reason
		if(s[len] == '\0' || s[len] == ' ')
			return true;
		s++;
	}
	return false;
}

void remove_new_line(char *buf)
{
	int len = DM_STRLEN(buf);
	if (len > 0 && buf[len - 1] == '\n')
		buf[len - 1] = 0;
}

static void dmcmd_exec(char *argv[])
{
	int devnull = open("/dev/null", O_RDWR);

	if (devnull == -1)
		exit(127);

	dup2(devnull, 0);
	dup2(devnull, 1);
	dup2(devnull, 2);

	if (devnull > 2)
		close(devnull);

	execvp(argv[0], argv); /* Flawfinder: ignore */
	exit(127);
}

int dmcmd(char *cmd, int n, ...)
{
	char *argv[n + 2];
	va_list arg;
	int i, status;
	pid_t pid, wpid;

	argv[0] = cmd;
	va_start(arg, n);
	for (i = 0; i < n; i++) {
		argv[i + 1] = va_arg(arg, char *);
	}
	va_end(arg);
	argv[n + 1] = NULL;

	if ((pid = fork()) == -1)
		return -1;

	if (pid == 0)
		dmcmd_exec(argv);

	do {
		wpid = waitpid(pid, &status, 0);
		if (wpid == pid) {
			if (WIFEXITED(status))
				return WEXITSTATUS(status);
			if (WIFSIGNALED(status))
				return 128 + WTERMSIG(status);
		}
	} while (wpid == -1 && errno == EINTR);

	return -1;
}

int dmcmd_no_wait(char *cmd, int n, ...)
{
	char *argv[n + 2];
	va_list arg;
	int i;
	pid_t pid;

	argv[0] = cmd;
	va_start(arg, n);
	for (i = 0; i < n; i++) {
		argv[i + 1] = va_arg(arg, char *);
	}
	va_end(arg);
	argv[n + 1] = NULL;

	if ((pid = fork()) == -1)
		return -1;

	if (pid == 0)
		dmcmd_exec(argv);

	return 0;
}

int run_cmd(const char *cmd, char *output, size_t out_len)
{
	int ret = -1;
	FILE *pp;

	if (cmd == NULL) {
		return 0;
	}

	if (output == NULL || out_len == 0) {
		return ret;
	}

	memset(output, 0, out_len);

	pp = popen(cmd, "r"); // Flawfinder: ignore
	if (pp != NULL) {
		if (!(fgets(output, out_len, pp) == NULL && ferror(pp) != 0)) {
			ret = 0;
		}
		pclose(pp);
	}

	return ret;
}

void hex_to_ip(char *address, char *ret, size_t size)
{
	unsigned int ip[4] = {0};

	sscanf(address, "%2x%2x%2x%2x", &(ip[0]), &(ip[1]), &(ip[2]), &(ip[3]));
	if (htonl(13) == 13) {
		snprintf(ret, size, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
	} else {
		snprintf(ret, size, "%u.%u.%u.%u", ip[3], ip[2], ip[1], ip[0]);
	}
}

/*
 * dmmap_config sections list manipulation
 */
void add_dmmap_config_dup_list(struct list_head *dup_list, struct uci_section *config_section, struct uci_section *dmmap_section)
{
	struct dm_data *dm_data = NULL;

	dm_data = dmcalloc(1, sizeof(struct dm_data));
	list_add_tail(&dm_data->list, dup_list);
	dm_data->config_section = config_section;
	dm_data->dmmap_section = dmmap_section;
}

void free_dmmap_config_dup_list(struct list_head *dup_list)
{
	struct dm_data *dm_data = NULL, *tmp = NULL;

	list_for_each_entry_safe(dm_data, tmp, dup_list, list) {
		list_del(&dm_data->list);
		dmfree(dm_data);
	}
}

/*
 * Function allows to synchronize config section with dmmap config
 */
struct uci_section *get_origin_section_from_config(char *package, char *section_type, char *orig_section_name)
{
	struct uci_section *s = NULL;

	uci_foreach_sections(package, section_type, s) {
		if (strcmp(section_name(s), orig_section_name) == 0)
			return s;
	}

	return NULL;
}

struct uci_section *get_origin_section_from_dmmap(char *package, char *section_type, char *orig_section_name)
{
	struct uci_section *s = NULL;

	uci_path_foreach_sections(bbfdm, package, section_type, s) {
		if (strcmp(section_name(s), orig_section_name) == 0)
			return s;
	}

	return NULL;
}

struct uci_section *get_dup_section_in_dmmap(char *dmmap_package, char *section_type, char *orig_section_name)
{
	struct uci_section *s;

	uci_path_foreach_sections(bbfdm, dmmap_package, section_type, s) {
		char *dmmap_sec_name = NULL;

		dmuci_get_value_by_section_string(s, "section_name", &dmmap_sec_name);

		if (DM_STRCMP(dmmap_sec_name, orig_section_name) == 0)
			return s;
	}

	return NULL;
}

struct uci_section *get_dup_section_in_config_opt(char *package, char *section_type, char *opt_name, char *opt_value)
{
	struct uci_section *s;

	uci_foreach_option_eq(package, section_type, opt_name, opt_value, s) {
		return s;
	}

	return NULL;
}

struct uci_section *get_dup_section_in_dmmap_opt(char *dmmap_package, char *section_type, char *opt_name, char *opt_value)
{
	struct uci_section *s;

	uci_path_foreach_option_eq(bbfdm, dmmap_package, section_type, opt_name, opt_value, s) {
		return s;
	}

	return NULL;
}

struct uci_section *get_dup_section_in_dmmap_eq(char *dmmap_package, char* section_type, char*sect_name, char *opt_name, char *opt_value)
{
	struct uci_section *s;
	char *v;

	uci_path_foreach_option_eq(bbfdm, dmmap_package, section_type, "section_name", sect_name, s) {
		dmuci_get_value_by_section_string(s, opt_name, &v);
		if (DM_STRCMP(v, opt_value) == 0)
			return s;
	}
	return NULL;
}

struct uci_section *get_section_in_dmmap_with_options_eq(char *dmmap_package, char *section_type, char *opt1_name, char *opt1_value, char *opt2_name, char *opt2_value)
{
	struct uci_section *s = NULL;

	uci_path_foreach_option_eq(bbfdm, dmmap_package, section_type, opt1_name, opt1_value, s) {
		char *value = NULL;

		dmuci_get_value_by_section_string(s, opt2_name, &value);
		if (DM_STRCMP(value, opt2_value) == 0)
			return s;
	}

	return NULL;
}

void synchronize_specific_config_sections_with_dmmap(char *package, char *section_type, char *dmmap_package, struct list_head *dup_list)
{
	struct uci_section *s, *stmp, *dmmap_sect;
	char *v;

	uci_foreach_sections(package, section_type, s) {
		/*
		 * create/update corresponding dmmap section that have same config_section link and using param_value_array
		 */
		if ((dmmap_sect = get_dup_section_in_dmmap(dmmap_package, section_type, section_name(s))) == NULL) {
			dmuci_add_section_bbfdm(dmmap_package, section_type, &dmmap_sect);
			dmuci_set_value_by_section_bbfdm(dmmap_sect, "section_name", section_name(s));
		}

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

	/*
	 * Delete unused dmmap sections
	 */
	uci_path_foreach_sections_safe(bbfdm, dmmap_package, section_type, stmp, s) {
		dmuci_get_value_by_section_string(s, "section_name", &v);
		if (get_origin_section_from_config(package, section_type, v) == NULL)
			dmuci_delete_by_section(s, NULL, NULL);
	}
}

void synchronize_specific_config_sections_with_dmmap_eq(char *package, char *section_type, char *dmmap_package, char* option_name, char* option_value, struct list_head *dup_list)
{
	struct uci_section *s, *stmp, *dmmap_sec;
	char *v;

	uci_foreach_option_eq(package, section_type, option_name, option_value, s) {
		/*
		 * create/update corresponding dmmap section that have same config_section link and using param_value_array
		 */
		if ((dmmap_sec = get_dup_section_in_dmmap(dmmap_package, section_type, section_name(s))) == NULL) {
			dmuci_add_section_bbfdm(dmmap_package, section_type, &dmmap_sec);
			dmuci_set_value_by_section_bbfdm(dmmap_sec, "section_name", section_name(s));
		}

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

	/*
	 * Delete unused dmmap sections
	 */
	uci_path_foreach_sections_safe(bbfdm, dmmap_package, section_type, stmp, s) {
		dmuci_get_value_by_section_string(s, "section_name", &v);
		if (get_origin_section_from_config(package, section_type, v) == NULL)
			dmuci_delete_by_section(s, NULL, NULL);
	}
}

void synchronize_specific_config_sections_with_dmmap_cont(char *package, char *section_type, char *dmmap_package, char* option_name, char* option_value, struct list_head *dup_list)
{
	struct uci_section *uci_s, *stmp, *dmmap_sect;
	char *v;

	uci_foreach_option_cont(package, section_type, option_name, option_value, uci_s) {
		/*
		 * create/update corresponding dmmap section that have same config_section link and using param_value_array
		 */
		if ((dmmap_sect = get_dup_section_in_dmmap(dmmap_package, section_type, section_name(uci_s))) == NULL) {
			dmuci_add_section_bbfdm(dmmap_package, section_type, &dmmap_sect);
			dmuci_set_value_by_section_bbfdm(dmmap_sect, "section_name", section_name(uci_s));
		}

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

	/*
	 * Delete unused dmmap sections
	 */
	uci_path_foreach_sections_safe(bbfdm, dmmap_package, section_type, stmp, uci_s) {
		dmuci_get_value_by_section_string(uci_s, "section_name", &v);
		if (get_origin_section_from_config(package, section_type, v) == NULL)
			dmuci_delete_by_section(uci_s, NULL, NULL);
	}
}

void get_dmmap_section_of_config_section(char* dmmap_package, char* section_type, char *section_name, struct uci_section **dmmap_section)
{
	struct uci_section *s;

	uci_path_foreach_option_eq(bbfdm, dmmap_package, section_type, "section_name", section_name, s) {
		*dmmap_section = s;
		return;
	}
	*dmmap_section = NULL;
}

void get_dmmap_section_of_config_section_eq(char* dmmap_package, char* section_type, char *opt, char* value, struct uci_section **dmmap_section)
{
	struct uci_section *s;

	uci_path_foreach_option_eq(bbfdm, dmmap_package, section_type, opt, value, s) {
		*dmmap_section = s;
		return;
	}
	*dmmap_section = NULL;
}

void get_dmmap_section_of_config_section_cont(char* dmmap_package, char* section_type, char *opt, char* value, struct uci_section **dmmap_section)
{
	struct uci_section *s;

	uci_path_foreach_option_cont(bbfdm, dmmap_package, section_type, opt, value, s) {
		*dmmap_section = s;
		return;
	}
	*dmmap_section = NULL;
}

void get_config_section_of_dmmap_section(char* package, char* section_type, char *section_name, struct uci_section **config_section)
{
	struct uci_section *s;

	uci_foreach_sections(package, section_type, s) {
		if (strcmp(section_name(s), section_name) == 0) {
			*config_section = s;
			return;
		}
	}
	*config_section = NULL;
}

char *check_create_dmmap_package(const char *dmmap_package)
{
	char *path;
	int rc;

	rc = dmasprintf(&path, "/etc/bbfdm/dmmap/%s", dmmap_package);
	if (rc == -1)
		return NULL;

	if (!file_exists(path)) {
		/*
		 *File does not exist
		 **/
		FILE *fp = fopen(path, "w"); // new empty file
		if (fp)
			fclose(fp);
	}
	return path;
}

struct uci_section *is_dmmap_section_exist(char* package, char* section)
{
	struct uci_section *s;

	uci_path_foreach_sections(bbfdm, package, section, s) {
		return s;
	}
	return NULL;
}

struct uci_section *is_dmmap_section_exist_eq(char* package, char* section, char* opt, char* value)
{
	struct uci_section *s;

	uci_path_foreach_option_eq(bbfdm, package, section, opt, value, s) {
		return s;
	}
	return NULL;
}

unsigned int count_occurrences(const char *str, char c)
{
	int count = 0;

	if (!str)
		return 0;

	char *pch = strchr(str, c);
	while (pch) {
		count++;
		pch = strchr(pch + 1, c);
	}

	return count;
}

bool isdigit_str(const char *str)
{
	if (!DM_STRLEN(str))
		return 0;

	while(isdigit(*str++));

	return (*(str-1)) ? 0 : 1;
}

bool ishex_str(const char *str)
{
	char *endptr = NULL;

	if (DM_STRLEN(str) < 2)
		return 0;

	if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X'))
		return 0;

	strtol(str, &endptr, 0);

	return DM_STRLEN(endptr) ? 0 : 1;
}

bool special_char(char c)
{
	if ((c >= 'a' && c <= 'z') ||
	    (c >= 'A' && c <= 'Z') ||
		(c >= '0' && c <= '9') ||
		(c == '_'))
		return false;
	else
		return true;
}

bool special_char_exits(const char *str)
{
	if (!DM_STRLEN(str))
		return false;

	while (!special_char(*str++));

	return (*(str-1)) ? true : false;
}

void replace_special_char(char *str, char c)
{
	for (int i = 0; i < DM_STRLEN(str); i++) {
		if (special_char(str[i]))
			str[i] = c;
	}
}

static inline int isword_delim(char c)
{
	if (c == ' ' ||
		c == ',' ||
		c == '.' ||
		c == '\t' ||
		c == '\v' ||
		c == '\r' ||
		c == '\n' ||
		c == '\0')
		return 1;
	return 0;
}

char *dm_strword(char *src, char *str)
{
	if (!src || src[0] == 0 || !str || str[0] == 0)
		return NULL;

	int len = strlen(str);
	char *ret = src;

	while ((ret = strstr(ret, str)) != NULL) {
		if ((ret == src && isword_delim(ret[len])) ||
			(ret != src && isword_delim(ret[len]) && isword_delim(*(ret - 1))))
			return ret;
		ret++;
	}
	return NULL;
}

char **strsplit(const char *str, const char *delim, size_t *numtokens)
{
	char *s = strdup(str);
	size_t tokens_alloc = 1;
	size_t tokens_used = 0;
	char **tokens = dmcalloc(tokens_alloc, sizeof(char*));
	char *token, *strtok_ctx;

	for (token = strtok_r(s, delim, &strtok_ctx);
		token != NULL;
		token = strtok_r(NULL, delim, &strtok_ctx)) {

		if (tokens_used == tokens_alloc) {
			tokens_alloc *= 2;
			tokens = dmrealloc(tokens, tokens_alloc * sizeof(char*));
		}
		tokens[tokens_used++] = dmstrdup(token);
	}
	if (tokens_used == 0) {
		dmfree(tokens);
		tokens = NULL;
	} else {
		tokens = dmrealloc(tokens, tokens_used * sizeof(char*));
	}
	*numtokens = tokens_used;
	FREE(s);
	return tokens;
}

void convert_str_to_uppercase(char *str)
{
	for (int i = 0; str[i] != '\0'; i++) {
		if (str[i] >= 'a' && str[i] <= 'z') {
			str[i] = str[i] - 32;
		}
	}
}

char *get_macaddr(char *interface_name)
{
	char *device = get_device(interface_name);
	char *mac;

	if (device[0]) {
		char file[128];
		char val[32];

		snprintf(file, sizeof(file), "/sys/class/net/%s/address", device);
		dm_read_sysfs_file(file, val, sizeof(val));
		convert_str_to_uppercase(val);
		mac = dmstrdup(val);
	} else {
		mac = "";
	}
	return mac;
}

char *get_device(char *interface_name)
{
	json_object *res;

	dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", interface_name, String}}, 1, &res);
	return dmjson_get_value(res, 1, "device");
}

char *get_l3_device(char *interface_name)
{
	json_object *res;

	dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", interface_name, String}}, 1, &res);
	return dmjson_get_value(res, 1, "l3_device");
}

bool value_exists_in_uci_list(struct uci_list *list, const char *value)
{
	struct uci_element *e = NULL;

	if (list == NULL || value == NULL)
		return false;

	uci_foreach_element(list, e) {
		if (!DM_STRCMP(e->name, value))
			return true;
	}

	return false;
}

bool value_exits_in_str_list(char *str_list, const char *delimitor, const char *str)
{
	char *pch = NULL, *spch = NULL;

	if (!DM_STRLEN(str_list) || !delimitor || !str)
		return false;

	char *list = dmstrdup(str_list);

	for (pch = strtok_r(list, delimitor, &spch); pch != NULL; pch = strtok_r(NULL, delimitor, &spch)) {
		if (DM_STRCMP(pch, str) == 0)
			return true;
	}

	return false;
}

char *add_str_to_str_list(char *str_list, const char *delimitor, const char *str)
{
	char *res = "";

	if (!str_list || !delimitor || !str)
		return "";

	dmasprintf(&res, "%s%s%s", str_list, strlen(str_list) ? delimitor : "", str);

	return res;
}

char *remove_str_from_str_list(char *str_list, const char *delimitor, const char *str)
{
	char *pch = NULL, *spch = NULL;
	unsigned pos = 0;

	if (!str_list || !delimitor || !str)
		return "";

	int len = strlen(str_list);
	int del_len = strlen(delimitor);

	char *res = (char *)dmcalloc(len + 1, sizeof(char));
	char *list = dmstrdup(str_list);

	for (pch = strtok_r(list, delimitor, &spch); pch != NULL; pch = strtok_r(NULL, delimitor, &spch)) {

		if (DM_LSTRCMP(str, pch) == 0)
			continue;

		pos += snprintf(&res[pos], len + 1 - pos, "%s%s", pch, delimitor);
	}

	dmfree(list);

	if (pos)
		res[pos - del_len] = 0;

	return res;
}

int get_shift_utc_time(int shift_time, char *utc_time, int size)
{
	struct tm *t_tm;
	time_t now = time(NULL);

	now = now + shift_time;
	t_tm = gmtime(&now);
	if (t_tm == NULL)
		return -1;

	if (strftime(utc_time, size, "%Y-%m-%dT%H:%M:%SZ", t_tm) == 0)
		return -1;

	return 0;
}

int get_shift_time_time(int shift_time, char *local_time, int size)
{
	time_t t_time;
	struct tm *t_tm;

	t_time = time(NULL) + shift_time;
	t_tm = localtime(&t_time);
	if (t_tm == NULL)
		return -1;

	if (strftime(local_time, size, "%Y-%m-%dT%H:%M:%SZ", t_tm) == 0)
		return -1;

	return 0;
}

static inline int char_is_valid(char c)
{
	return c >= 0x20 && c < 0x7f;
}

int dm_read_sysfs_file(const char *file, char *dst, unsigned len)
{
	char content[len];
	int fd;
	int rlen;
	int i, n;
	int rc = 0;

	dst[0] = 0;

	fd = open(file, O_RDONLY);
	if (fd == -1)
		return -1;

	rlen = read(fd, content, len - 1);
	if (rlen == -1) {
		rc = -1;
		goto out;
	}

	content[rlen] = 0;
	for (i = 0, n = 0; i < rlen; i++) {
		if (!char_is_valid(content[i])) {
			if (i == 0)
				continue;
			else
				break;
		}
		dst[n++] = content[i];
	}
	dst[n] = 0;

out:
	close(fd);
	return rc;
}

int get_net_device_sysfs(const char *device, const char *name, char **value)
{
	if (device && device[0]) {
		char file[256];
		char val[32] = {0};

		snprintf(file, sizeof(file), "/sys/class/net/%s/%s", device, name);
		dm_read_sysfs_file(file, val, sizeof(val));
		if (strcmp(name, "address") == 0) {
			// Convert the mac address to upper case
			convert_str_to_uppercase(val);
		}
		*value = dmstrdup(val);
	} else {
		*value = "0";
	}
	return 0;
}

int get_net_device_status(const char *device, char **value)
{
	char *operstate = NULL;

	get_net_device_sysfs(device, "operstate", &operstate);
	if (operstate == NULL || *operstate == '\0') {
		*value = "Down";
		return 0;
	}

	if (strcmp(operstate, "up") == 0)
		*value = "Up";
	else if (strcmp(operstate, "unknown") == 0)
		*value = "Unknown";
	else if (strcmp(operstate, "notpresent") == 0)
		*value = "NotPresent";
	else if (strcmp(operstate, "lowerlayerdown") == 0)
		*value = "LowerLayerDown";
	else if (strcmp(operstate, "dormant") == 0)
		*value = "Dormant";
	else
		*value = "Down";

	return 0;
}

int get_net_iface_sysfs(const char *uci_iface, const char *name, char **value)
{
	const char *device = get_device((char *)uci_iface);

	return get_net_device_sysfs(device, name, value);
}

int dm_time_utc_format(time_t ts, char **dst)
{
	char time_buf[32] = { 0, 0 };
	struct tm *t_tm;

	*dst = "0001-01-01T00:00:00Z";

	t_tm = gmtime(&ts);
	if (t_tm == NULL)
		return -1;

	if(strftime(time_buf, sizeof(time_buf), "%Y-%m-%dT%H:%M:%SZ", t_tm) == 0)
		return -1;

	*dst = dmstrdup(time_buf);
	return 0;
}

int dm_time_format(time_t ts, char **dst)
{
	char time_buf[32] = { 0, 0 };
	struct tm *t_tm;

	*dst = "0001-01-01T00:00:00+00:00";

	t_tm = localtime(&ts);
	if (t_tm == NULL)
		return -1;

	if(strftime(time_buf, sizeof(time_buf), "%Y-%m-%dT%H:%M:%S%z", t_tm) == 0)
		return -1;

	time_buf[25] = time_buf[24];
	time_buf[24] = time_buf[23];
	time_buf[22] = ':';
	time_buf[26] = '\0';

	*dst = dmstrdup(time_buf);
	return 0;
}

void convert_string_to_hex(const char *str, char *hex, size_t size)
{
	int i, len = DM_STRLEN(str);
	unsigned pos = 0;

	for (i = 0; i < len && pos < size - 2; i++) {
		pos += snprintf((char *)hex + pos, size - pos, "%02X", str[i]);
	}

	hex[pos] = '\0';
}

void convert_hex_to_string(const char *hex, char *str, size_t size)
{
	int i, len = DM_STRLEN(hex);
	unsigned pos = 0;
	char buf[3] = {0};

	for (i = 0; i < len && pos < size - 1; i += 2) {
		DM_STRNCPY(buf, &hex[i], 3);

		char c = (char)strtol(buf, NULL, 16);
		if (!char_is_valid(c))
			continue;

		pos += snprintf((char *)str + pos, size - pos, "%c", c);
	}

	str[pos] = '\0';
}

bool match(const char *string, const char *pattern, size_t nmatch, regmatch_t pmatch[])
{
	regex_t re;

	if (regcomp(&re, pattern, REG_EXTENDED) != 0)
		return 0;

	int status = regexec(&re, string, nmatch, pmatch, 0);

	regfree(&re);

	return (status != 0) ? false : true;
}

void bbfdm_set_fault_message(struct dmctx *ctx, const char *format, ...)
{
	va_list args;
	int len = DM_STRLEN(ctx->fault_msg);

	if (len)
		return;

	va_start(args, format);
	vsnprintf(ctx->fault_msg, sizeof(ctx->fault_msg), format, args); // Flawfinder: ignore
	va_end(args);
}

static int bbfdm_validate_string_length(struct dmctx *ctx, char *value, int min_length, int max_length)
{
	if ((min_length > 0) && (DM_STRLEN(value) < min_length)) {
		bbfdm_set_fault_message(ctx, "The length of '%s' value must be greater than '%d'.", value, min_length);
		return -1;
	}

	if ((max_length > 0) && (DM_STRLEN(value) > max_length)) {
		bbfdm_set_fault_message(ctx, "The length of '%s' value must be lower than '%d'.", value, max_length);
		return -1;
	}

	return 0;
}

static int bbfdm_validate_string_enumeration(struct dmctx *ctx, char *value, char *enumeration[])
{
	for (; *enumeration; enumeration++) {
		if (DM_STRCMP(*enumeration, value) == 0)
			return 0;
	}

	bbfdm_set_fault_message(ctx, "List enumerations did not include '%s' value", value);
	return -1;
}

static int bbfdm_validate_string_pattern(struct dmctx *ctx, char *value, char *pattern[])
{
	for (; *pattern; pattern++) {
		if (match(value, *pattern, 0, NULL))
			return 0;
	}

	bbfdm_set_fault_message(ctx, "List patterns did not match '%s' value", value);
	return -1;
}

int bbfdm_validate_string(struct dmctx *ctx, char *value, int min_length, int max_length, char *enumeration[], char *pattern[])
{
	/* check size */
	if (bbfdm_validate_string_length(ctx, value, min_length, max_length))
		return -1;

	/* check enumeration */
	if (enumeration && bbfdm_validate_string_enumeration(ctx, value, enumeration))
		return -1;

	/* check pattern */
	if (pattern && bbfdm_validate_string_pattern(ctx, value, pattern))
		return -1;

	return 0;
}

int bbfdm_validate_boolean(struct dmctx *ctx, char *value)
{
	/* check format */
	if ((value[0] == '1' && value[1] == '\0') ||
		(value[0] == '0' && value[1] == '\0') ||
		!strcasecmp(value, "true") ||
		!strcasecmp(value, "false")) {
		return 0;
	}

	bbfdm_set_fault_message(ctx, "'%s' value must be ['boolean']. Acceptable values are ['true', 'false', '1', '0'].", value);
	return -1;
}

int bbfdm_validate_unsignedInt(struct dmctx *ctx, char *value, struct range_args r_args[], int r_args_size)
{
	if (!value || value[0] == 0) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check size for each range */
	for (int i = 0; i < r_args_size; i++) {
		unsigned long ui_val = 0, minval = 0, maxval = 0;
		char *endval = NULL, *endmin = NULL, *endmax = NULL;

		if (r_args[i].min) minval = strtoul(r_args[i].min, &endmin, 10);
		if (r_args[i].max) maxval = strtoul(r_args[i].max, &endmax, 10);

		/* reset errno to 0 before call */
		errno = 0;

		ui_val = strtoul(value, &endval, 10);

		if ((*value == '-') || (*endval != 0) || (errno != 0)) {
			bbfdm_set_fault_message(ctx, "'%s' value is not a real unsigned integer", value);
			return -1;
		}

		if (r_args[i].min && r_args[i].max) {

			if (minval == maxval) {
				if (strlen(value) == minval)
					break;
			} else {
				if (ui_val >= minval && ui_val <= maxval)
					break;
			}

			if (i == r_args_size - 1) {
				bbfdm_set_fault_message(ctx, "'%s' value is not within range (min: '%s' max: '%s')", value, r_args[i].min, r_args[i].max);
				return -1;
			}

			continue;
		}

		/* check size */
		if (r_args[i].min && ui_val < minval) {
			bbfdm_set_fault_message(ctx, "'%lu' value must be greater than '%s'.", ui_val, r_args[i].min);
			return -1;
		}

		if (r_args[i].max && ui_val > maxval) {
			bbfdm_set_fault_message(ctx, "'%lu' value must be lower than '%s'.", ui_val, r_args[i].max);
			return -1;
		}

		if (ui_val > (unsigned int)UINT_MAX) {
			bbfdm_set_fault_message(ctx, "'%lu' value must be lower than '%u'.", ui_val, (unsigned int)UINT_MAX);
			return -1;
		}
	}

	return 0;
}

int bbfdm_validate_int(struct dmctx *ctx, char *value, struct range_args r_args[], int r_args_size)
{
	if (!value || value[0] == 0) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check size for each range */
	for (int i = 0; i < r_args_size; i++) {
		long i_val = 0, minval = 0, maxval = 0;
		char *endval = NULL, *endmin = NULL, *endmax = NULL;

		if (r_args[i].min) minval = strtol(r_args[i].min, &endmin, 10);
		if (r_args[i].max) maxval = strtol(r_args[i].max, &endmax, 10);

		/* reset errno to 0 before call */
		errno = 0;

		i_val = strtol(value, &endval, 10);

		if ((*endval != 0) || (errno != 0)) {
			bbfdm_set_fault_message(ctx, "'%s' value is not a real integer", value);
			return -1;
		}

		if (r_args[i].min && r_args[i].max) {

			if (i_val >= minval && i_val <= maxval)
				break;

			if (i == r_args_size - 1) {
				bbfdm_set_fault_message(ctx, "'%s' value is not within range (min: '%s' max: '%s')", value, r_args[i].min, r_args[i].max);
				return -1;
			}

			continue;
		}

		/* check size */
		if (r_args[i].min && i_val < minval) {
			bbfdm_set_fault_message(ctx, "'%ld' value must be greater than '%s'.", i_val, r_args[i].min);
			return -1;
		}

		if (r_args[i].max && i_val > maxval) {
			bbfdm_set_fault_message(ctx, "'%ld' value must be lower than '%s'.", i_val, r_args[i].max);
			return -1;
		}

		if ((i_val < INT_MIN) || (i_val > INT_MAX)) {
			bbfdm_set_fault_message(ctx, "'%ld' value is not within range (min: '%d' max: '%d')", i_val, INT_MIN, INT_MAX);
			return -1;
		}
	}

	return 0;
}

int bbfdm_validate_unsignedLong(struct dmctx *ctx, char *value, struct range_args r_args[], int r_args_size)
{
	if (!value || value[0] == 0) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check size for each range */
	for (int i = 0; i < r_args_size; i++) {
		unsigned long ul_val = 0, minval = 0, maxval = 0;
		char *endval = NULL, *endmin = NULL, *endmax = NULL;

		if (r_args[i].min) minval = strtoul(r_args[i].min, &endmin, 10);
		if (r_args[i].max) maxval = strtoul(r_args[i].max, &endmax, 10);

		/* reset errno to 0 before call */
		errno = 0;

		ul_val = strtoul(value, &endval, 10);

		if ((*value == '-') || (*endval != 0) || (errno != 0)) {
			bbfdm_set_fault_message(ctx, "'%s' value is not a real unsigned long", value);
			return -1;
		}

		if (r_args[i].min && r_args[i].max) {

			if (ul_val >= minval && ul_val <= maxval)
				break;

			if (i == r_args_size - 1) {
				bbfdm_set_fault_message(ctx, "'%s' value is not within range (min: '%s' max: '%s')", value, r_args[i].min, r_args[i].max);
				return -1;
			}

			continue;
		}

		/* check size */
		if (r_args[i].min && ul_val < minval) {
			bbfdm_set_fault_message(ctx, "'%lu' value must be greater than '%s'.", ul_val, r_args[i].min);
			return -1;
		}

		if (r_args[i].max && ul_val > maxval) {
			bbfdm_set_fault_message(ctx, "'%lu' value must be lower than '%s'.", ul_val, r_args[i].max);
			return -1;
		}

		if (ul_val > (unsigned long)ULONG_MAX) {
			bbfdm_set_fault_message(ctx, "'%lu' value must be lower than '%lu'.", ul_val, (unsigned long)ULONG_MAX);
			return -1;
		}
	}

	return 0;
}

int bbfdm_validate_long(struct dmctx *ctx, char *value, struct range_args r_args[], int r_args_size)
{
	if (!value || value[0] == 0) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check size for each range */
	for (int i = 0; i < r_args_size; i++) {
		long u_val = 0, minval = 0, maxval = 0;
		char *endval = NULL, *endmin = NULL, *endmax = NULL;

		if (r_args[i].min) minval = strtol(r_args[i].min, &endmin, 10);
		if (r_args[i].max) maxval = strtol(r_args[i].max, &endmax, 10);

		/* reset errno to 0 before call */
		errno = 0;

		u_val = strtol(value, &endval, 10);

		if ((*endval != 0) || (errno != 0)) {
			bbfdm_set_fault_message(ctx, "'%s' value is not a real long", value);
			return -1;
		}

		if (r_args[i].min && r_args[i].max) {

			if (u_val >= minval && u_val <= maxval)
				break;

			if (i == r_args_size - 1) {
				bbfdm_set_fault_message(ctx, "'%s' value is not within range (min: '%s' max: '%s')", value, r_args[i].min, r_args[i].max);
				return -1;
			}

			continue;
		}

		/* check size */
		if (r_args[i].min && u_val < minval) {
			bbfdm_set_fault_message(ctx, "'%ld' value must be greater than '%s'.", u_val, r_args[i].min);
			return -1;
		}

		if (r_args[i].max && u_val > maxval) {
			bbfdm_set_fault_message(ctx, "'%ld' value must be lower than '%s'.", u_val, r_args[i].max);
			return -1;
		}
	}

	return 0;
}

int bbfdm_validate_dateTime(struct dmctx *ctx, char *value)
{
	/*
	 * Allowed format:
	 * XXXX-XX-XXTXX:XX:XXZ
	 * XXXX-XX-XXTXX:XX:XX.XXXZ
	 * XXXX-XX-XXTXX:XX:XX.XXXXXXZ
	 */

	char *p = NULL;
	struct tm tm;
	int m;

	p = strptime(value, "%Y-%m-%dT%H:%M:%SZ", &tm);
	if (p && *p == '\0')
		return 0;

	p = strptime(value, "%Y-%m-%dT%H:%M:%S.", &tm);
	if (!p || *p == '\0' || value[DM_STRLEN(value) - 1] != 'Z') {
		bbfdm_set_fault_message(ctx, "'%s' value must be ['dateTime']. Acceptable formats are ['XXXX-XX-XXTXX:XX:XXZ', 'XXXX-XX-XXTXX:XX:XX.XXXZ', 'XXXX-XX-XXTXX:XX:XX.XXXXXXZ'].", value);
		return -1;
	}

	int num_parsed = sscanf(p, "%dZ", &m);
	if (num_parsed != 1 || (DM_STRLEN(p) != 7 && DM_STRLEN(p) != 4)) {
		bbfdm_set_fault_message(ctx, "'%s' value must be ['dateTime']. Acceptable formats are ['XXXX-XX-XXTXX:XX:XXZ', 'XXXX-XX-XXTXX:XX:XX.XXXZ', 'XXXX-XX-XXTXX:XX:XX.XXXXXXZ'].", value);
		return -1;
	}

	return 0;
}

int bbfdm_validate_hexBinary(struct dmctx *ctx, char *value, struct range_args r_args[], int r_args_size)
{
	int i;

	/* check format */
	for (i = 0; i < DM_STRLEN(value); i++) {
		if (!isxdigit(value[i])) {
			bbfdm_set_fault_message(ctx, "'%s' value is not a real hexBinary", value);
			return -1;
		}
	}

	/* check size */
	for (i = 0; i < r_args_size; i++) {

		if (r_args[i].min && r_args[i].max && (DM_STRTOL(r_args[i].min) == DM_STRTOL(r_args[i].max))) {

			if (DM_STRLEN(value) == 2 * DM_STRTOL(r_args[i].max))
				break;

			if (i == r_args_size - 1) {
				bbfdm_set_fault_message(ctx, "The length of '%s' value is not within range (min: '%s' max: '%s')", value, r_args[i].min, r_args[i].max);
				return -1;
			}

			continue;
		}

		if (r_args[i].min && (DM_STRLEN(value) < DM_STRTOL(r_args[i].min))) {
			bbfdm_set_fault_message(ctx, "The length of '%s' value must be greater than '%s'.", value, r_args[i].min);
			return -1;
		}

		if (r_args[i].max && (DM_STRLEN(value) > DM_STRTOL(r_args[i].max))) {
			bbfdm_set_fault_message(ctx, "The length of '%s' value must be lower than '%s'.", value, r_args[i].max);
			return -1;
		}
	}

	return 0;
}

static int bbfdm_validate_size_list(struct dmctx *ctx, int min_item, int max_item, int nbr_item)
{
	if (((min_item > 0) && (max_item > 0) && (min_item == max_item) && (nbr_item == 2 * min_item)))
		return 0;

	if ((min_item > 0) && (nbr_item < min_item)) {
		bbfdm_set_fault_message(ctx, "The number of item of '%d' list must be greater than '%d'.", nbr_item, min_item);
		return -1;
	}

	if ((max_item > 0) && (nbr_item > max_item)) {
		bbfdm_set_fault_message(ctx, "The number of item of '%d' list must be lower than '%d'.", nbr_item, max_item);
		return -1;
	}

	return 0;
}

int bbfdm_validate_string_list(struct dmctx *ctx, char *value, int min_item, int max_item, int max_size, int min, int max, char *enumeration[], char *pattern[])
{
	char *pch, *pchr;
	int nbr_item = 0;

	if (!value) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check length of list */
	if ((max_size > 0) && (strlen(value) > max_size)) {
		bbfdm_set_fault_message(ctx, "The length of '%s' list must be lower than '%d'.", value, max_size);
		return -1;
	}

	/* copy data in buffer */
	char buf[strlen(value)+1];
	DM_STRNCPY(buf, value, sizeof(buf));

	/* for each value, validate string */
	for (pch = strtok_r(buf, ",", &pchr); pch != NULL; pch = strtok_r(NULL, ",", &pchr)) {
		if (bbfdm_validate_string(ctx, pch, min, max, enumeration, pattern))
			return -1;
		nbr_item ++;
	}

	/* check size of list */
	if (bbfdm_validate_size_list(ctx, min_item, max_item, nbr_item))
		return -1;

	return 0;
}

int bbfdm_validate_unsignedInt_list(struct dmctx *ctx, char *value, int min_item, int max_item, int max_size, struct range_args r_args[], int r_args_size)
{
	char *tmp, *saveptr;
	int nbr_item = 0;

	if (!value) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check length of list */
	if ((max_size > 0) && (strlen(value) > max_size)) {
		bbfdm_set_fault_message(ctx, "The length of '%s' list must be lower than '%d'.", value, max_size);
		return -1;
	}

	/* copy data in buffer */
	char buf[strlen(value)+1];
	DM_STRNCPY(buf, value, sizeof(buf));

	/* for each value, validate string */
	for (tmp = strtok_r(buf, ",", &saveptr); tmp != NULL; tmp = strtok_r(NULL, ",", &saveptr)) {
		if (bbfdm_validate_unsignedInt(ctx, tmp, r_args, r_args_size))
			return -1;
		nbr_item ++;
	}

	/* check size of list */
	if (bbfdm_validate_size_list(ctx, min_item, max_item, nbr_item))
		return -1;

	return 0;
}

int bbfdm_validate_int_list(struct dmctx *ctx, char *value, int min_item, int max_item, int max_size, struct range_args r_args[], int r_args_size)
{
	char *token, *pchr;
	int nbr_item = 0;

	if (!value) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check length of list */
	if ((max_size > 0) && (strlen(value) > max_size)) {
		bbfdm_set_fault_message(ctx, "The length of '%s' list must be lower than '%d'.", value, max_size);
		return -1;
	}

	/* copy data in buffer */
	char buf[strlen(value)+1];
	DM_STRNCPY(buf, value, sizeof(buf));

	/* for each value, validate string */
	for (token = strtok_r(buf, ",", &pchr); token != NULL; token = strtok_r(NULL, ",", &pchr)) {
		if (bbfdm_validate_int(ctx, token, r_args, r_args_size))
			return -1;
		nbr_item ++;
	}

	/* check size of list */
	if (bbfdm_validate_size_list(ctx, min_item, max_item, nbr_item))
		return -1;

	return 0;
}

int bbfdm_validate_unsignedLong_list(struct dmctx *ctx, char *value, int min_item, int max_item, int max_size, struct range_args r_args[], int r_args_size)
{
	char *token, *tmp;
	int nbr_item = 0;

	if (!value) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check length of list */
	if ((max_size > 0) && (strlen(value) > max_size)) {
		bbfdm_set_fault_message(ctx, "The length of '%s' list must be lower than '%d'.", value, max_size);
		return -1;
	}

	/* copy data in buffer */
	char buf[strlen(value)+1];
	DM_STRNCPY(buf, value, sizeof(buf));

	/* for each value, validate string */
	for (token = strtok_r(buf, ",", &tmp); token != NULL; token = strtok_r(NULL, ",", &tmp)) {
		if (bbfdm_validate_unsignedLong(ctx, token, r_args, r_args_size))
			return -1;
		nbr_item ++;
	}

	/* check size of list */
	if (bbfdm_validate_size_list(ctx, min_item, max_item, nbr_item))
		return -1;

	return 0;
}

int bbfdm_validate_long_list(struct dmctx *ctx, char *value, int min_item, int max_item, int max_size, struct range_args r_args[], int r_args_size)
{
	char *pch, *saveptr;
	int nbr_item = 0;

	if (!value) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check length of list */
	if ((max_size > 0) && (strlen(value) > max_size)) {
		bbfdm_set_fault_message(ctx, "The length of '%s' list must be lower than '%d'.", value, max_size);
		return -1;
	}

	/* copy data in buffer */
	char buf[strlen(value)+1];
	DM_STRNCPY(buf, value, sizeof(buf));

	/* for each value, validate string */
	for (pch = strtok_r(buf, ",", &saveptr); pch != NULL; pch = strtok_r(NULL, ",", &saveptr)) {
		if (bbfdm_validate_long(ctx, pch, r_args, r_args_size))
			return -1;
		nbr_item ++;
	}

	/* check size of list */
	if (bbfdm_validate_size_list(ctx, min_item, max_item, nbr_item))
		return -1;

	return 0;
}

int bbfdm_validate_hexBinary_list(struct dmctx *ctx, char *value, int min_item, int max_item, int max_size, struct range_args r_args[], int r_args_size)
{
	char *pch, *spch;
	int nbr_item = 0;

	if (!value) {
		bbfdm_set_fault_message(ctx, "Value should not be blank.");
		return -1;
	}

	/* check length of list */
	if ((max_size > 0) && (strlen(value) > max_size)) {
		bbfdm_set_fault_message(ctx, "The length of '%s' list must be lower than '%d'.", value, max_size);
		return -1;
	}

	/* copy data in buffer */
	char buf[strlen(value)+1];
	DM_STRNCPY(buf, value, sizeof(buf));

	/* for each value, validate string */
	for (pch = strtok_r(buf, ",", &spch); pch != NULL; pch = strtok_r(NULL, ",", &spch)) {
		if (bbfdm_validate_hexBinary(ctx, pch, r_args, r_args_size))
			return -1;
		nbr_item ++;
	}

	/* check size of list */
	if (bbfdm_validate_size_list(ctx, min_item, max_item, nbr_item))
		return -1;

	return 0;
}

bool folder_exists(const char *path)
{
	struct stat buffer;

	return stat(path, &buffer) == 0 && S_ISDIR(buffer.st_mode);
}

bool file_exists(const char *path)
{
	struct stat buffer;

	return stat(path, &buffer) == 0;
}

bool is_regular_file(const char *path)
{
	struct stat buffer;

	return stat(path, &buffer) == 0 && S_ISREG(buffer.st_mode);
}

unsigned long file_system_size(const char *path, const enum fs_size_type_enum type)
{
	struct statvfs vfs;

	statvfs(path, &vfs);

	switch (type) {
		case FS_SIZE_TOTAL:
			return vfs.f_blocks * vfs.f_frsize;
		case FS_SIZE_AVAILABLE:
			return vfs.f_bavail * vfs.f_frsize;
		case FS_SIZE_USED:
			return (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize;
		default:
			return -1;
	}
}

static int get_base64_char(char b64)
{
	char *base64C = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

	for (int i = 0; i < 64; i++)
		if (base64C[i] == b64)
		return i;

	return -1;
}

char *base64_decode(const char *src)
{
	int i, j = 0;

	if (!src || *src == '\0')
		return "";

	size_t decsize = DM_STRLEN(src)*6/8;
	char *out = (char *)dmmalloc((decsize +1) * sizeof(char));

	for (i = 0; i < DM_STRLEN(src)-1; i++) {
		out[j] = (get_base64_char(src[i]) << (j%3==0?2:(j%3==1?4:6))) + (get_base64_char(src[i+1]) >> (j%3==0?4:(j%3==1? 2:0)));
		if (j%3 == 2)
			i++;
		j++;
	}
	out[j] = '\0';

	return out;
}

void string_to_mac(const char *str, size_t str_len, char *out, size_t out_len)
{
	unsigned pos = 0;
	int i, j;

	if (!str || !str_len)
		return;

	for (i = 0, j = 0; i < str_len; ++i, j += 3) {
		pos += snprintf(out + j, out_len - pos, "%02x", str[i] & 0xff);
		if (i < str_len - 1)
			pos += snprintf(out + j + 2, out_len - pos, "%c", ':');
	}
}

void remove_char(char *str, const char c)
{
	int i = 0, j = 0;

	if (DM_STRLEN(str) == 0)
		return;

	while (str[i]) {
		if (str[i] != c)
			str[j++] = str[i];
		i++;
	}

	str[j] = 0;
}

char *replace_char(char *str, char find, char replace)
{
	char *current_pos = DM_STRCHR(str, find);
	while (current_pos) {
		*current_pos = replace;
		current_pos = DM_STRCHR(current_pos, find);
	}
	return str;
}

/**
 * Replace all occurrences of a substring in a given string with another substring.
 *
 * @param input_str The input string where replacements will be performed.
 * @param old_substr The substring to be replaced.
 * @param new_substr The substring to replace `old_substr`.
 * @param result_str The buffer to store the result. If NULL, memory will be allocated.
 * @param buffer_len The length of the buffer. If `result_str` is not NULL, this should be the size of the buffer.
 * @return A pointer to the result string. If `result_str` is provided, it will point to `result_str`, otherwise, it will be dynamically allocated.
 */
char *replace_str(const char *input_str, const char *old_substr, const char *new_substr, char *result_str, size_t buffer_len)
{
	if (result_str && buffer_len > 0)
		result_str[0] = 0;

	if (!input_str || !old_substr || !new_substr || (result_str && buffer_len == 0))
		return NULL;

	size_t input_str_len = strlen(input_str);
	size_t old_substr_len = strlen(old_substr);
	size_t new_substr_len = strlen(new_substr);
	size_t occurrences = 0;

	if (input_str_len == 0) {
		// Handle case where the input string is empty
		if (result_str && buffer_len > 0) {
			return result_str;
		} else {
			return strdup("");
		}
	}

	if (old_substr_len == 0) {
		// Handle case where the input substring is empty
		if (result_str && buffer_len > 0) {
			snprintf(result_str, buffer_len, "%s", input_str);
			return result_str;
		} else {
			return strdup(input_str);
		}
	}

	// Count occurrences of old_substr in input_str
	for (size_t i = 0; i<input_str_len; i++) {
		if (strstr(&input_str[i], old_substr) == &input_str[i]) {
			occurrences++;
			i += old_substr_len;
		}
	}

	size_t new_str_len = input_str_len + occurrences * (new_substr_len - old_substr_len) + 1;

	if (result_str && buffer_len > 0 && new_str_len > buffer_len) {
		// Buffer size is too small
		return NULL;
	}

	// Allocate memory only if result_str is not provided
	char *result = result_str ? result_str : (char *)malloc(new_str_len * sizeof(char));

	if (!result) {
		// Memory allocation failed
		return NULL;
	}

	size_t i = 0;
	while (*input_str) {
		char *tmp = strstr(input_str, old_substr);
		if (tmp == input_str) {
			// Replace old_substr with new_substr
			strncpy(&result[i], new_substr, new_substr_len);
			i += new_substr_len;
			input_str += old_substr_len;
		} else if (tmp) {
			// Copy characters from input_str to result until the match
			size_t len = tmp - input_str;
			strncpy(&result[i], input_str, len);
			i += len;
			input_str += len;
		} else {
			// No more occurrences, copy the remaining characters
			result[i++] = *input_str++;
		}
	}
	result[i] = '\0';

	return result;
}

void strip_lead_trail_whitespace(char *str)
{
	if (str == NULL)
		return;

	/* First remove leading whitespace */
	const char* first_valid = str;

	while (*first_valid == ' ') {
		++first_valid;
	}

	size_t len = strlen(first_valid) + 1;

	memmove(str, first_valid, len);

	/* Now remove trailing whitespace */
	char* end_str = str + strlen(str) - 1;

	while (str < end_str  && *end_str == ' ') {
		*end_str = '\0';
		--end_str ;
	}
}

int dm_buf_to_file(char *buf, const char *filename)
{
	FILE *file;
	int ret = -1;

	if (buf == NULL || filename == NULL)
		return ret;

	file = fopen(filename, "w");
	if (file) {
		ret = fputs(buf, file);
		fclose(file);
	}

	return ret;
}

int dm_file_to_buf(const char *filename, void *buf, size_t buf_size)
{
	FILE *file;
	int ret = -1;

	file = fopen(filename, "r");
	if (file) {
		ret = fread(buf, 1, buf_size - 1, file);
		fclose(file);
	}
	((char *)buf)[ret > 0 ? ret : 0] = '\0';
	return ret;
}

int dm_file_copy(char *src, char *dst)
{
	size_t n;
	char buf[1024];
	int ret = -1;
	FILE *file_src = NULL, *file_dst = NULL;

	if (DM_STRLEN(src) == 0 || DM_STRLEN(dst) == 0) {
		return -1;
	}

	file_src = fopen(src, "r");
	if (!file_src)
		goto exit;

	file_dst = fopen(dst, "w");
	if (!file_dst)
		goto exit;

	while ((n = fread(buf, 1, sizeof(buf), file_src)) > 0) {
		if (fwrite(buf, 1, n, file_dst) != n)
			goto exit;
	}

	ret = 0;
exit:
	if (file_dst)
		fclose(file_dst);
	if (file_src)
		fclose(file_src);
	return ret;
}

int parse_proc_intf6_line(const char *line, const char *device, char *ipstr, size_t str_len)
{
	char ip6buf[INET6_ADDRSTRLEN] = {0}, dev[32] = {0};
	unsigned int ip[4], prefix;

	sscanf(line, "%8x%8x%8x%8x %*s %x %*s %*s %31s",
				&ip[0], &ip[1], &ip[2], &ip[3],
				&prefix, dev);

	if (DM_STRCMP(dev, device) != 0)
		return -1;

	ip[0] = htonl(ip[0]);
	ip[1] = htonl(ip[1]);
	ip[2] = htonl(ip[2]);
	ip[3] = htonl(ip[3]);

	inet_ntop(AF_INET6, ip, ip6buf, INET6_ADDRSTRLEN);
	snprintf(ipstr, str_len, "%s/%u", ip6buf, prefix);

	if (strncmp(ipstr, "fe80:", 5) != 0)
		return -1;

	return 0;
}

char *diagnostics_get_option(char *sec_name, char *option)
{
	char *value = NULL;
	dmuci_get_option_value_string_bbfdm(DMMAP_DIAGNOSTIGS, sec_name, option, &value);
	return value;
}

char *diagnostics_get_option_fallback_def(char *sec_name, char *option, char *default_value)
{
	char *value = diagnostics_get_option(sec_name, option);
	return (*value != '\0') ? value : default_value;
}

void diagnostics_set_option(char *sec_name, char *option, char *value)
{
	check_create_dmmap_package(DMMAP_DIAGNOSTIGS);
	struct uci_section *section = dmuci_walk_section_bbfdm(DMMAP_DIAGNOSTIGS, sec_name, NULL, NULL, CMP_SECTION, NULL, NULL, GET_FIRST_SECTION);
	if (!section)
		dmuci_set_value_bbfdm(DMMAP_DIAGNOSTIGS, sec_name, "", sec_name);

	dmuci_set_value_bbfdm(DMMAP_DIAGNOSTIGS, sec_name, option, value);
}

void diagnostics_reset_state(char *sec_name)
{
	char *diag_state = diagnostics_get_option(sec_name, "DiagnosticState");
	if (strcmp(diag_state, "Requested") != 0) {
		diagnostics_set_option(sec_name, "DiagnosticState", "None");
	}
}

char *diagnostics_get_interface_name(struct dmctx *ctx, char *value)
{
	char *linker = NULL;

	if (!value || *value == 0)
		return "";

	if (strncmp(value, "Device.IP.Interface.", 20) != 0)
		return "";

	bbfdm_operate_reference_linker(ctx, value, &linker);
	return linker ? linker : "";
}

long download_file(char *file_path, const char *url, const char *username, const char *password)
{
	long res_code = 0;

	if (!file_path || !url)
		return -1;

	if (strncmp(url, FILE_URI, strlen(FILE_URI)) == 0) {

		const char *curr_path = (!strncmp(url, FILE_LOCALHOST_URI, strlen(FILE_LOCALHOST_URI))) ? url + strlen(FILE_LOCALHOST_URI) : url + strlen(FILE_URI);

		if (!file_exists(curr_path))
			return -1;

		DM_STRNCPY(file_path, curr_path, 256);
	} else {

		CURL *curl = curl_easy_init();
		if (curl) {
			curl_easy_setopt(curl, CURLOPT_URL, url);
			if (username) curl_easy_setopt(curl, CURLOPT_USERNAME, username);
			if (password) curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
			curl_easy_setopt(curl, CURLOPT_TIMEOUT, 600);

			FILE *fp = fopen(file_path, "wb");
			if (fp) {
				curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
				curl_easy_perform(curl);
				fclose(fp);
			}

			curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code);
			curl_easy_cleanup(curl);
		}
	}

	return res_code;
}

long upload_file(const char *file_path, const char *url, const char *username, const char *password)
{
	long res_code = 0;

	if (!file_path || !url)
		return -1;

	if (strncmp(url, FILE_URI, strlen(FILE_URI)) == 0) {
		char dst_path[2046] = {0};
		char buff[BUFSIZ] = {0};
		FILE *sfp, *dfp;
		size_t n;

		sfp = fopen(file_path, "rb");
		if (sfp == NULL) {
			return -1;
		}

		snprintf(dst_path, sizeof(dst_path), "%s", url + strlen(FILE_URI));
		dfp = fopen(dst_path, "wb");
		if (dfp == NULL) {
			fclose(sfp);
			return -1;
		}

		while ((n = fread(buff, 1, BUFSIZ, sfp)) != 0) {
			fwrite(buff, 1, n, dfp);
		}

		fclose(sfp);
		fclose(dfp);
	} else {
		CURL *curl = curl_easy_init();
		if (curl) {
			curl_easy_setopt(curl, CURLOPT_URL, url);
			if (username) curl_easy_setopt(curl, CURLOPT_USERNAME, username);
			if (password) curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
			curl_easy_setopt(curl, CURLOPT_TIMEOUT, 600);
			curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

			FILE *fp = fopen(file_path, "rb");
			if (fp) {
				curl_easy_setopt(curl, CURLOPT_READDATA, fp);
				curl_easy_perform(curl);
				fclose(fp);
			}

			curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code);
			curl_easy_cleanup(curl);
		}
	}

	return res_code;
}
