/*
 * config.c - load/store icwmp application configuration
 *
 * Copyright (C) 2021-2022, IOPSYS Software Solutions AB.
 *
 *	  Author Mohamed Kallel <mohamed.kallel@pivasoftware.com>
 *	  Author Ahmed Zribi <ahmed.zribi@pivasoftware.com>
 *	  Author Omar Kallel <omar.kallel@pivasoftware.com>
 *
 * See LICENSE file for license related information.
 *
 */

#include <string.h>
#include <stdlib.h>
#include <fcntl.h>

#include "common.h"
#include "config.h"
#include "log.h"
#include "reboot.h"
#include "uci_utils.h"
#include "ubus_utils.h"
#include "ssl_utils.h"
#include "datamodel_interface.h"
#include "heartbeat.h"
#include "cwmp_http.h"

static bool dhcp_discovered_acs()
{
	char dhcp_url[BUF_SIZE_2048] = {0};
	get_uci_path_value(NULL, "cwmp.acs.dhcp_url", dhcp_url, BUF_SIZE_2048);

	if (CWMP_STRLEN(cwmp_ctx.conf.acs_url) == 0)
		return false;

	if (CWMP_STRCMP(dhcp_url, cwmp_ctx.conf.acs_url) == 0) {
		// CWMP trying to reach with DHCP discovered ACS url
		return true;
	}

	return false;
}

int get_preinit_config()
{
	char value[BUF_SIZE_256] = {0};

	get_uci_path_value(NULL, UCI_LOG_SEVERITY_PATH, value, BUF_SIZE_256);
	log_set_severity_idx(value);

	get_uci_path_value(NULL, UCI_CPE_LOG_FILE_NAME, value, BUF_SIZE_256);
	log_set_log_file_name(value);

	get_uci_path_value(NULL, UCI_CPE_LOG_MAX_SIZE, value, BUF_SIZE_256);
	log_set_file_max_size(value);

	get_uci_path_value(NULL, UCI_CPE_ENABLE_STDOUT_LOG, value, BUF_SIZE_256);
	log_set_on_console(value);

	get_uci_path_value(NULL, UCI_CPE_ENABLE_FILE_LOG, value, BUF_SIZE_256);
	log_set_on_file(value);

	get_uci_path_value(NULL, UCI_CPE_ENABLE_SYSLOG, value, BUF_SIZE_256);
	log_set_on_syslog(value);

	get_uci_path_value(NULL, UCI_CPE_DEFAULT_WAN_IFACE, cwmp_ctx.conf.default_wan_iface, BUF_SIZE_32);
	if (CWMP_STRLEN(cwmp_ctx.conf.default_wan_iface) == 0) {
		CWMP_STRNCMP(cwmp_ctx.conf.default_wan_iface, "wan", sizeof(cwmp_ctx.conf.default_wan_iface));
	}

	cwmp_ctx.conf.amd_version = DEFAULT_AMD_VERSION;
	get_uci_path_value(NULL, UCI_CPE_AMD_VERSION, value, BUF_SIZE_256);
	if (CWMP_STRLEN(value) != 0) {
		int a = (int)strtol(value, NULL, 10);
		cwmp_ctx.conf.amd_version = (a >= 1 && a <= 6) ? a : DEFAULT_AMD_VERSION;
	}

	cwmp_ctx.conf.supported_amd_version = cwmp_ctx.conf.amd_version;

	get_uci_path_value(NULL, UCI_CPE_CERT_PATH, cwmp_ctx.conf.cpe_client_cert, BUF_SIZE_256);
	get_uci_path_value(NULL, UCI_CPE_KEY_PATH, cwmp_ctx.conf.cpe_client_key, BUF_SIZE_256);

	CWMP_LOG(DEBUG, "CWMP CONFIG - default wan interface: %s", cwmp_ctx.conf.default_wan_iface);
	CWMP_LOG(DEBUG, "CWMP CONFIG - amendement version: %d", cwmp_ctx.conf.amd_version);
	CWMP_LOG(DEBUG, "CWMP CONFIG - cpe cert path: %s", cwmp_ctx.conf.cpe_client_cert);
	CWMP_LOG(DEBUG, "CWMP CONFIG - cpe key path: %s", cwmp_ctx.conf.cpe_client_key);

	return CWMP_OK;
}


static void global_conf_init()
{
	get_global_config();

	/* Launch reboot methods if needed */
	launch_reboot_methods();
}

void cwmp_config_load()
{
	int error;
	struct timeval start_time, current_time;

	global_conf_init();

	cwmp_ctx.net.ipv6_status = is_ipv6_enabled();
	error = icwmp_check_http_connection();
	if (error != CWMP_OK && dhcp_discovered_acs() == true) {
		gettimeofday(&start_time, NULL);
	}

	while (error != CWMP_OK) {
		if (dhcp_discovered_acs() == true) {
			gettimeofday(&current_time, NULL);

			long long start_microseconds = (long long)start_time.tv_sec * 1000000 + start_time.tv_usec;
			long long current_microseconds = (long long)current_time.tv_sec * 1000000 + current_time.tv_usec;
			long long elapsed_microseconds = current_microseconds - start_microseconds;
			long long elapsed_seconds = elapsed_microseconds / 1000000;

			if (elapsed_seconds >= 300) {
				// CWMP not able to connect DHCP discovered ACS url for 300 seconds
				// let's renew the lease in search of new ACS URL from DHCP server
				if (cwmp_ctx.conf.dhcp_discovery == false) {
					set_uci_path_value(NULL, "cwmp.acs.dhcp_discovery", "1");
				}

				// Renew DHCP lease
				if (CWMP_STRLEN(cwmp_ctx.conf.default_wan_iface) != 0) {
					struct blob_buf b = {0};

					CWMP_MEMSET(&b, 0, sizeof(struct blob_buf));
					blob_buf_init(&b, 0);
					blobmsg_add_string(&b, "interface", cwmp_ctx.conf.default_wan_iface);

					icwmp_ubus_invoke("network.interface", "renew", b.head, NULL, NULL);
					blob_buf_free(&b);
				}

				// Reset start time
				gettimeofday(&start_time, NULL);
			}
		}

		CWMP_LOG(DEBUG, "Init: failed to check http connection");
		sleep(UCI_OPTION_READ_INTERVAL);
		global_conf_init();
		cwmp_ctx.net.ipv6_status = is_ipv6_enabled();
		error = icwmp_check_http_connection();
	}

	if (dhcp_discovered_acs() == true) {
		// CWMP connected to DHCP discovered ACS URL
		if (cwmp_ctx.conf.dhcp_discovery == true) {
			set_uci_path_value(NULL, "cwmp.acs.dhcp_discovery", "0");
		}

		char cur_url[BUF_SIZE_2048] = {0};
		get_uci_path_value(NULL, "cwmp.acs.url", cur_url, BUF_SIZE_2048);

		if (CWMP_STRCMP(cwmp_ctx.conf.acs_url, cur_url) != 0) {
			set_uci_path_value(NULL, "cwmp.acs.url", cwmp_ctx.conf.acs_url);
		}
	}
}

static void cwmp_get_device_info(const char *param, char *value, size_t size) {
	struct cwmp_dm_parameter dm_param = {0};

	if (param == NULL || value == NULL)
		return;

	if (strlen(value) != 0)
		return;

	cwmp_get_parameter_value(param, &dm_param);

	while (CWMP_STRLEN(dm_param.value) == 0) {
		CWMP_LOG(ERROR, "Init: failed to get value of %s", param);
		cwmp_get_parameter_value(param, &dm_param);
	}

	snprintf(value, size, "%s", dm_param.value);
}

int cwmp_get_deviceid()
{
	cwmp_get_device_info("Device.DeviceInfo.Manufacturer", cwmp_ctx.deviceid.manufacturer, sizeof(cwmp_ctx.deviceid.manufacturer));
	cwmp_get_device_info("Device.DeviceInfo.SerialNumber", cwmp_ctx.deviceid.serialnumber, sizeof(cwmp_ctx.deviceid.serialnumber));
	cwmp_get_device_info("Device.DeviceInfo.ProductClass", cwmp_ctx.deviceid.productclass, sizeof(cwmp_ctx.deviceid.productclass));
	cwmp_get_device_info("Device.DeviceInfo.ManufacturerOUI", cwmp_ctx.deviceid.oui, sizeof(cwmp_ctx.deviceid.oui));
	cwmp_get_device_info("Device.DeviceInfo.SoftwareVersion", cwmp_ctx.deviceid.softwareversion, sizeof(cwmp_ctx.deviceid.softwareversion));

	return CWMP_OK;
}

int cwmp_config_reload()
{
	CWMP_MEMSET(&cwmp_ctx.env, 0, sizeof(struct env));

	global_conf_init();

	return CWMP_OK;
}
