/*
 * download.c - Download method functions
 *
 * Copyright (C) 2022, IOPSYS Software Solutions AB.
 *
 * 		Author: Omar Kallel <omar.kallel@pivasoftware.com>
 *
 * See LICENSE file for license related information.
 *
 */
#include <curl/curl.h>
#include <string.h>
#include <libubox/blobmsg_json.h>

#include "download.h"
#include "ubus_utils.h"
#include "backupSession.h"
#include "log.h"
#include "event.h"
#include "common.h"
#include "subprocess.h"
#include "session.h"
#include "uci_utils.h"

#ifndef BBF_VENDOR_PREFIX
#define BBF_VENDOR_PREFIX "X_IOWRT_EU_"
#endif

#define MACRO_ENGINE "/sbin/macro_engine.sh"

LIST_HEAD(list_download);
LIST_HEAD(list_schedule_download);

struct fwbank_dump {
	int bank_id;
	int status;
};

int count_download_queue = 0;

/*
 * Download File
 */
int download_file(const char *file_path, const char *url, const char *username, const char *password, const char *interface)
{
	if (url == NULL) {
		CWMP_LOG(ERROR, "download %s: no url specified", __FUNCTION__);
		return -1;
	}
	int res_code = 0;
	CURL *curl = curl_easy_init();
	if (curl) {
		curl_easy_setopt(curl, CURLOPT_URL, url);
		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
		curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
		if (username && CWMP_STRLEN(username) > 0) {
			char userpass[1024];
			snprintf(userpass, sizeof(userpass), "%s:%s", username, password);
			curl_easy_setopt(curl, CURLOPT_USERPWD, userpass);
		}
		if (CWMP_STRNCMP(url, "https://", 8) == 0)
			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_ANY);
		curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 10000L);
		curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
		curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, 1L);

		// Use l3 interface name
		if (interface && CWMP_STRLEN(interface))
			curl_easy_setopt(curl, CURLOPT_INTERFACE, interface);

		if (file_path == NULL)
			file_path = "/tmp/download_file";

		// cppcheck-suppress cert-MSC24-C
		FILE *fp = fopen(file_path, "wb");
		if (fp) {
			CURLcode res;

			curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
			res = curl_easy_perform(curl);
			if (res != CURLE_OK) {
				CWMP_LOG(WARNING, "Curl failed [%s], file[%s]", curl_easy_strerror(res), url);
			}
			fclose(fp);
		}
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code);

		curl_easy_cleanup(curl);
	}

	return res_code;
}

char *download_file_task_function(char *task)
{

	struct blob_buf bbuf;
	CWMP_MEMSET(&bbuf, 0, sizeof(struct blob_buf));
	blob_buf_init(&bbuf, 0);

	if (task == NULL) {
		CWMP_LOG(ERROR, "download %s: task is null", __FUNCTION__);
		return NULL;
	}
	if (blobmsg_add_json_from_string(&bbuf, task) == false) {
		blob_buf_free(&bbuf);
		return NULL;
	}
	const struct blobmsg_policy p[6] = { { "task", BLOBMSG_TYPE_STRING }, { "file_path", BLOBMSG_TYPE_STRING }, { "url", BLOBMSG_TYPE_STRING }, { "username", BLOBMSG_TYPE_STRING }, { "password", BLOBMSG_TYPE_STRING }, {"interface", BLOBMSG_TYPE_STRING} };

	struct blob_attr *tb[6] = { NULL, NULL, NULL, NULL, NULL};
	blobmsg_parse(p, 6, tb, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head));
	char *task_name = blobmsg_get_string(tb[0]);
	if (CWMP_STRCMP(task_name, "download") != 0)
		return NULL;
	char *file_path = blobmsg_get_string(tb[1]);
	char *url = blobmsg_get_string(tb[2]);
	char *username = blobmsg_get_string(tb[3]);
	char *password = blobmsg_get_string(tb[4]);
	char *interface = blobmsg_get_string(tb[5]);

	int http_code = download_file(file_path, url, username, password, interface);
	char *http_ret = (char *)malloc(4 * sizeof(char));
	snprintf(http_ret, 4, "%d", http_code);
	http_ret[3] = 0;
	CWMP_LOG(DEBUG, "download return code [%d=>%s]", http_code, http_ret)
	return http_ret;
}

int download_file_in_subprocess(const char *file_path, const char *url, const char *username, const char *password)
{
	if (CWMP_OK != subprocess_start(download_file_task_function)) {
		CWMP_LOG(ERROR, "Failed to spawn subprocess to start download");
		return 500;
	}

	if (url == NULL) {
		CWMP_LOG(ERROR, "download %s: url is null");
		return 500;
	}

	struct blob_buf bbuf;
	CWMP_MEMSET(&bbuf, 0, sizeof(struct blob_buf));
	blob_buf_init(&bbuf, 0);
	blobmsg_add_string(&bbuf, "task", "download");
	blobmsg_add_string(&bbuf, "file_path", file_path ? file_path : "");
	blobmsg_add_string(&bbuf, "url", url ? url : "");
	blobmsg_add_string(&bbuf, "username", username ? username : "");
	blobmsg_add_string(&bbuf, "password", password ? password : "");

	if (cwmp_ctx.net.use_curl_ifname && CWMP_STRLEN(cwmp_ctx.net.interface))
		blobmsg_add_string(&bbuf, "interface", cwmp_ctx.net.interface);

	char *download_task = blobmsg_format_json(bbuf.head, true);
	blob_buf_free(&bbuf);

	if (download_task != NULL) {
		char *ret = execute_task_in_subprocess(download_task);
		FREE(download_task);
		return ret ? (int)strtol(ret, NULL, 10) : 500;
	}

	return 500;
}
/*
 * Check if the downloaded image can be applied
 */
void ubus_check_image_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
	const struct blobmsg_policy p[1] = {{ "code", BLOBMSG_TYPE_INT32 }};
	struct blob_attr *tb[1] = { NULL };
	int *code;

	if (!req) {
		CWMP_LOG(ERROR, "image_check: req is null");
		return;
	}

	code = (int *)req->priv;

	if (msg == NULL) {
		*code = 2;
		CWMP_LOG(ERROR, "image_check: msg is null");
		return;
	}

	if (blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg)) != 0) {
		*code = 3;
		CWMP_LOG(ERROR, "image_check: msg parse error");
		return;
	}

	*code = tb[0] ? blobmsg_get_u32(tb[0]) : 4;
}

int cwmp_check_image(const char *fw_path)
{
	int code = 3;
	char cmd[128] = {0};

	CWMP_LOG(INFO, "Check downloaded image [%s] ...", fw_path);

	if (CWMP_STRLEN(fw_path) == 0) {
		CWMP_LOG(INFO, "Firmware path is empty");
		return code;
	}

	snprintf(cmd, sizeof(cmd), "sysupgrade -T %s", fw_path);

	code = run_cmd(cmd, NULL, 0);
	if (code != 0) {
		CWMP_LOG(INFO, "Firmware verification failed with err: %d", code);
	}

	return code;
}

/*
 * Get available bank
 */
void ubus_get_available_bank_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
	if (msg == NULL) {
		CWMP_LOG(ERROR, "download: msg is null");
		return;
	}
	int *bank_id = (int *)req->priv;
	struct blob_attr *banks = NULL;
	struct blob_attr *cur;
	int rem;

	blobmsg_for_each_attr(cur, msg, rem)
	{
		if (blobmsg_type(cur) == BLOBMSG_TYPE_ARRAY) {
			banks = cur;
			break;
		}
	}

	const struct blobmsg_policy p[8] = { { "name", BLOBMSG_TYPE_STRING },  { "id", BLOBMSG_TYPE_INT32 },	 { "active", BLOBMSG_TYPE_BOOL },  { "upgrade", BLOBMSG_TYPE_BOOL },
					     { "fwver", BLOBMSG_TYPE_STRING }, { "swver", BLOBMSG_TYPE_STRING }, { "fwver", BLOBMSG_TYPE_STRING }, { "status", BLOBMSG_TYPE_STRING } };

	blobmsg_for_each_attr(cur, banks, rem)
	{
		struct blob_attr *tb[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
		blobmsg_parse(p, 8, tb, blobmsg_data(cur), blobmsg_len(cur));
		if (!tb[0])
			continue;

		if (blobmsg_get_bool(tb[2]) == false) {
			*bank_id = blobmsg_get_u32(tb[1]);
			break;
		}
	}
}

int get_available_bank_id()
{
	int bank_id = 0, e;
	struct blob_buf b = { 0 };
	CWMP_MEMSET(&b, 0, sizeof(struct blob_buf));
	blob_buf_init(&b, 0);

	e = icwmp_ubus_invoke("fwbank", "dump", b.head, ubus_get_available_bank_callback, &bank_id);
	if (e != 0) {
		CWMP_LOG(INFO, "fwbank dump ubus method failed: Ubus err code: %d", e);
	}

	blob_buf_free(&b);
	return bank_id;
}

static void ubus_set_bootbank_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
	struct blob_attr *tb[1] = { NULL };
	const struct blobmsg_policy p[1] = {{ "success", BLOBMSG_TYPE_BOOL }};
	int *status = (req) ? (int *)req->priv : NULL;

	if (msg == NULL) {
		CWMP_LOG(ERROR, "%s: msg is null", __func__);
		return;
	}

	blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
	if (tb[0]) {
		if (status) {
			*status = blobmsg_get_bool(tb[0]);
		}
	}
}

static bool set_fwbank_bootbank(int id)
{
	struct blob_buf b = { 0 };
	int e;
	bool status = false;

	CWMP_MEMSET(&b, 0, sizeof(struct blob_buf));
	blob_buf_init(&b, 0);
	blobmsg_add_u32(&b, "bank", id);

	e = icwmp_ubus_invoke("fwbank", "set_bootbank", b.head, ubus_set_bootbank_callback, &status);
	if (e != 0) {
		CWMP_LOG(INFO, "fwbank dump ubus method failed: Ubus err code: %d", e);
		status = false;
	}

	blob_buf_free(&b);
	return status;
}

/*
 * Get Bank Status
 */
void ubus_get_bank_status_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
	if (msg == NULL) {
		CWMP_LOG(ERROR, "download %s: msg is null");
		return;
	}
	struct fwbank_dump *bank = (struct fwbank_dump *)req->priv;
	bool bank_found = false;
	struct blob_attr *banks = NULL;
	struct blob_attr *cur;
	int rem;

	blobmsg_for_each_attr(cur, msg, rem)
	{
		if (blobmsg_type(cur) == BLOBMSG_TYPE_ARRAY) {
			banks = cur;
			break;
		}
	}

	const struct blobmsg_policy p[8] = { { "name", BLOBMSG_TYPE_STRING },  { "id", BLOBMSG_TYPE_INT32 },	 { "active", BLOBMSG_TYPE_BOOL },  { "upgrade", BLOBMSG_TYPE_BOOL },
					     { "fwver", BLOBMSG_TYPE_STRING }, { "swver", BLOBMSG_TYPE_STRING }, { "fwver", BLOBMSG_TYPE_STRING }, { "status", BLOBMSG_TYPE_STRING } };

	blobmsg_for_each_attr(cur, banks, rem)
	{
		struct blob_attr *tb[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
		blobmsg_parse(p, 8, tb, blobmsg_data(cur), blobmsg_len(cur));
		if (!tb[0])
			continue;

		if (blobmsg_get_u32(tb[1]) == (uint32_t)bank->bank_id) {
			bank_found = true;
			char *status = blobmsg_get_string(tb[7]);
			if (CWMP_STRCMP(status, "Available") == 0 || CWMP_STRCMP(status, "Active") == 0)
				bank->status = 1;
			else
				bank->status = 0;
		}
	}
	if (bank_found == false)
		bank->status = 0;
}

/*
 * Apply the web content
 */
static void ubus_get_download_status(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
	if (msg == NULL) {
		CWMP_LOG(ERROR, "received msg is null");
		return;
	}

	bool *status = (bool *)req->priv;
	if (status == NULL)
		return;

	const struct blobmsg_policy p[1] = { { "success", BLOBMSG_TYPE_BOOL } };
	struct blob_attr *tb[1] = { NULL };
	blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
	if (tb[0] == NULL) {
		CWMP_LOG(ERROR, "status not exists in received msg");
		return;
	}

	*status = blobmsg_get_bool(tb[0]);
}

bool cwmp_apply_web_content(char *filepath)
{
	int e;
	bool status = false;
	struct blob_buf b = { 0 };
	CWMP_MEMSET(&b, 0, sizeof(struct blob_buf));
	blob_buf_init(&b, 0);
	blobmsg_add_string(&b, "url", filepath ? filepath: "");
	blobmsg_add_string(&b, "filetype", "2 Web Content");

	CWMP_LOG(INFO, "Apply downloaded web content ...");
	e = icwmp_ubus_invoke("cwmp.rpc", "download", b.head, ubus_get_download_status, &status);
	if (e != 0) {
		CWMP_LOG(INFO, "web-content install ubus method failed: Ubus err code: %d", e);
		status = false;
	}

	blob_buf_free(&b);
	return status;
}

// Apply opconf
static void ubus_get_opconf_status(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
	if (msg == NULL) {
		CWMP_LOG(ERROR, "received msg is null");
		return;
	}

	char **status = (char **)req->priv;
	if (status == NULL)
		return;

	const struct blobmsg_policy p[1] = { { "Result", BLOBMSG_TYPE_STRING } };
	struct blob_attr *tb[1] = { NULL };
	blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
	if (tb[0] == NULL) {
		CWMP_LOG(ERROR, "result not exists in received msg");
		return;
	}

	*status = strdup(blobmsg_get_string(tb[0]));
}

bool is_downloaded_file_opconf(char *file_name)
{
	FILE *f;
	bool is_opconf = false;

	if (file_name == NULL) {
		return false;
	}

	// cppcheck-suppress cert-MSC24-C
	f = fopen(file_name, "rb");
	if (!f) {
		CWMP_LOG(ERROR, "Failed to open downloaded config[%s] file", file_name);
		return false;
	}

	is_opconf = is_file_json(f);
	if (is_opconf)
		goto exit;

	is_opconf = is_file_tar(f);
	if (is_opconf)
		goto exit;

	is_opconf = is_file_gzip(f);
exit:
	fclose(f);
	return is_opconf;
}

#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
int cwmp_apply_opconf(char *filepath, enum download_filetypes filetype)
{
	char file_url[520];
	char *status = NULL;

	if (CWMP_STRLEN(filepath) == 0)
		return CWMP_GEN_ERR;

	snprintf(file_url, sizeof(file_url), "file://%s", filepath);

	struct blob_buf b = {0};
	CWMP_MEMSET(&b, 0, sizeof(struct blob_buf));
	blob_buf_init(&b, 0);

	blobmsg_add_string(&b, "url", file_url);
	if (filetype == DOWNLOAD_FILETYPE_OPERATOR_CONF_FILE) {
		blobmsg_add_u8(&b, "persistent", true);
	}

	int err = icwmp_ubus_invoke("opconf", "install", b.head, ubus_get_opconf_status, &status);
	if (CWMP_STRCMP(status, "OK") != 0) {
		err = -1;
	}

	CWMP_LOG(DEBUG, "Opconf install type %d, status [%s]", filetype, status);
	blob_buf_free(&b);
	FREE(status);

	return err;
}
#endif

void fw_upgrade_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
	char **fault = (char **)req->priv;
	const struct blobmsg_policy p[1] = { { "results", BLOBMSG_TYPE_ARRAY } };
	struct blob_attr *tb[1] = { NULL };
	blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
	if (tb[0]) {
		struct blob_attr *attr = NULL;
		int rem = 0;

		blobmsg_for_each_attr(attr, tb[0], rem) {
			const struct blobmsg_policy p1[1] = { { "output", BLOBMSG_TYPE_ARRAY } };
			struct blob_attr *tb1[1] = { NULL };
			blobmsg_parse(p1, 1, tb1, blobmsg_data(attr), blobmsg_len(attr));

			if (tb1[0]) {
				struct blob_attr *param = NULL;
				int len = 0;

				blobmsg_for_each_attr(param, tb1[0], len) {
					const struct blobmsg_policy p2[1] = { { "fault", BLOBMSG_TYPE_INT32 } };
					struct blob_attr *tb2[1] = { NULL };
					blobmsg_parse(p2, 1, tb2, blobmsg_data(param), blobmsg_len(param));

					if (tb2[0]) {
						*fault = strdup("9010");
						return;
					}
				}
			}
		}
		*fault = NULL;
	} else {
		*fault = strdup("9010");
	}
}

static int cwmp_apply_multiple_firmware(enum download_filetypes filetype, bool auto_activate)
{
	char *fault_code = NULL;
	int e;
	struct blob_buf b = { 0 };
	int bank_id = get_available_bank_id();
	char buffer[32] = {0};
	int keep_opconf = -1, keep_userconf = -1;
	const char *config_scope = NULL;

	if (bank_id <= 0)
		return -1;

	char path[128] = {0}, url[128] = {0};
	snprintf(path, sizeof(path), "Device.DeviceInfo.FirmwareImage.%d.Download()", bank_id);
	snprintf(url, sizeof(url), "file://%s", FIRMWARE_UPGRADE_IMAGE);

	CWMP_MEMSET(&b, 0, sizeof(struct blob_buf));
	blob_buf_init(&b, 0);

	bb_add_string(&b, "path", path);
	void *tbl = blobmsg_open_table(&b, "input");
	bb_add_string(&b, "URL", url);
	//blobmsg_add_u8(&b, "AutoActivate", auto_activate);

	if (filetype == DOWNLOAD_FILETYPE_FIRMWARE_UPGRADE_IMAGE || filetype == DOWNLOAD_FILETYPE_STORED_FIRMWARE_IMAGE) {
		if (cwmp_ctx.conf.cpe_keep_config != -1) {
			snprintf(buffer, sizeof(buffer), "%s%s", BBF_VENDOR_PREFIX, "KeepConfig");
			blobmsg_add_u8(&b, buffer, cwmp_ctx.conf.cpe_keep_config);
			keep_userconf = (int)cwmp_ctx.conf.cpe_keep_config;
		}

		if (cwmp_ctx.conf.cpe_keep_opconf != -1) {
			snprintf(buffer, sizeof(buffer), "%s%s", BBF_VENDOR_PREFIX, "KeepOpConf");
			blobmsg_add_u8(&b, buffer, cwmp_ctx.conf.cpe_keep_opconf);
			keep_opconf = cwmp_ctx.conf.cpe_keep_opconf;
		}

		if (strlen(cwmp_ctx.conf.cpe_config_scope) != 0) {
			snprintf(buffer, sizeof(buffer), "%s%s", BBF_VENDOR_PREFIX, "ConfigScope");
			bb_add_string(&b, buffer, cwmp_ctx.conf.cpe_config_scope);
			config_scope = cwmp_ctx.conf.cpe_config_scope;
		}
#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
	} else if (filetype == DOWNLOAD_FILETYPE_UPGRADE_WITH_USERCONF) {
		snprintf(buffer, sizeof(buffer), "%s%s", BBF_VENDOR_PREFIX, "ConfigScope");
		bb_add_string(&b, buffer, "UserOnly");
		config_scope = "UserOnly";
	} else if (filetype == DOWNLOAD_FILETYPE_UPGRADE_WITH_FULLCONF) {
		snprintf(buffer, sizeof(buffer), "%s%s", BBF_VENDOR_PREFIX, "ConfigScope");
		bb_add_string(&b, buffer, "All");
		config_scope = "All";
	} else if (filetype == DOWNLOAD_FILETYPE_UPGRADE_WITH_NOCONF) {
		snprintf(buffer, sizeof(buffer), "%s%s", BBF_VENDOR_PREFIX, "KeepConfig");
		blobmsg_add_u8(&b, buffer, false);
		keep_userconf = 0;
#endif
	}

	blobmsg_close_table(&b, tbl);

	e = icwmp_ubus_invoke_timeout(BBFDM_OBJECT_NAME, "operate", b.head, fw_upgrade_callback, &fault_code, 120000);
	if (e != 0 || CWMP_STRLEN(fault_code) != 0) {
		CWMP_LOG(INFO, "fwbank upgrade ubus method failed: Ubus err code: %d, fault code: %s", e, fault_code ? fault_code : "");
		FREE(fault_code);
		return -2;
	}

	//set /var/state 'switch_bank' option
	set_uci_path_value(VARSTATE_CONFIG, "icwmp.cpe.switch_bank", "1");
#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
	if (keep_opconf != -1) {
		snprintf(buffer, sizeof(buffer), "%d", keep_opconf);
		set_uci_path_value(VARSTATE_CONFIG, "icwmp.cpe.KeepOpConf", buffer);
	}

	if (keep_userconf != -1) {
		snprintf(buffer, sizeof(buffer), "%d", keep_userconf);
		set_uci_path_value(VARSTATE_CONFIG, "icwmp.cpe.KeepConfig", buffer);
	}

	if (CWMP_STRLEN(config_scope)) {
		set_uci_path_value(VARSTATE_CONFIG, "icwmp.cpe.ConfigScope", config_scope);
	}
#endif
	blob_buf_free(&b);
	if (auto_activate == true) {
		bool status = set_fwbank_bootbank(bank_id);
		if (status == false) {
			CWMP_LOG(ERROR, "Failed to activate bank %d", bank_id);
			return -1;
		}
	}

	return CWMP_OK;
}

int cwmp_launch_download(struct download *pdownload, char *download_file_name, enum load_type ltype, struct transfer_complete **ptransfer_complete)
{
	int error = FAULT_CPE_NO_FAULT;
	char download_startTime[26] = {0};
	char download_endTime[26] = {0};
	struct transfer_complete *p;
	char err_msg[256] = {0};

	get_time(time(NULL), download_startTime, sizeof(download_startTime));
	bkp_session_delete_element((ltype == TYPE_DOWNLOAD) ? "download" : "schedule_download", pdownload->id);
	bkp_session_save();

	if (flashsize < pdownload->file_size) {
		error = FAULT_CPE_DOWNLOAD_FAILURE;
		snprintf(err_msg, sizeof(err_msg), "File size (%u) is larger than flash size (%u)", pdownload->file_size, flashsize);
		goto end_download;
	}

	int http_code = download_file_in_subprocess(ICWMP_DOWNLOAD_FILE, pdownload->url, pdownload->username, pdownload->password);
	if (http_code == 404) {
		error = FAULT_CPE_DOWNLOAD_FAIL_CONTACT_SERVER;
		snprintf(err_msg, sizeof(err_msg), "Failed to contact the file server (err_code: %d)", http_code);
	} else if (http_code == 401) {
		error = FAULT_CPE_DOWNLOAD_FAIL_FILE_AUTHENTICATION;
		snprintf(err_msg, sizeof(err_msg), "File server authentication failed (err_code: %d)", http_code);
	} else if (http_code != 200) {
		error = FAULT_CPE_DOWNLOAD_FAILURE;
		snprintf(err_msg, sizeof(err_msg), "File download failed (err_code: %d)", http_code);
	}

	if (error != FAULT_CPE_NO_FAULT)
		goto end_download;

	if (pdownload->file_type == NULL) {
		error = FAULT_CPE_INVALID_ARGUMENTS;
		snprintf(err_msg, sizeof(err_msg), "[%s:%d] File type [%d] is not a valid value", __func__, __LINE__, pdownload->filetype);
		goto end_download;
	}

	pdownload->filetype = get_download_filetype(pdownload->file_type);
	switch (pdownload->filetype) {
		case DOWNLOAD_FILETYPE_FIRMWARE_UPGRADE_IMAGE:
		case DOWNLOAD_FILETYPE_STORED_FIRMWARE_IMAGE:
#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
		case DOWNLOAD_FILETYPE_UPGRADE_WITH_NOCONF:
		case DOWNLOAD_FILETYPE_UPGRADE_WITH_USERCONF:
		case DOWNLOAD_FILETYPE_UPGRADE_WITH_FULLCONF:
#endif
			rename(ICWMP_DOWNLOAD_FILE, FIRMWARE_UPGRADE_IMAGE);
			int ret = cwmp_check_image(FIRMWARE_UPGRADE_IMAGE);

			if (ret == 0) {
				unsigned int file_size = get_file_size(FIRMWARE_UPGRADE_IMAGE);

				if (file_size > flashsize) {
					error = FAULT_CPE_DOWNLOAD_FAILURE;
					snprintf(err_msg, sizeof(err_msg), "File size: (%u) is larger than flash size: (%u)", file_size, flashsize);;
					remove_file(FIRMWARE_UPGRADE_IMAGE);
					goto end_download;
				} else {
					error = FAULT_CPE_NO_FAULT;
					goto end_download;
				}
			} else {
				error = FAULT_CPE_DOWNLOAD_FAIL_FILE_CORRUPTED;
				snprintf(err_msg, sizeof(err_msg), "Failed validation with %d of Downloaded file", ret);
				remove_file(FIRMWARE_UPGRADE_IMAGE);
			}
			break;
		case DOWNLOAD_FILETYPE_WEB_CONTENT:
			if (download_file_name != NULL) {
				char file_path[512];
				snprintf(file_path, sizeof(file_path), "/tmp/%s", download_file_name);
				rename(ICWMP_DOWNLOAD_FILE, file_path);
			} else
				rename(ICWMP_DOWNLOAD_FILE, WEB_CONTENT_FILE);

			error = FAULT_CPE_NO_FAULT;
			goto end_download;
			break;
		case DOWNLOAD_FILETYPE_VENDOR_CONFIG:
#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
		case DOWNLOAD_FILETYPE_OPERATOR_CONF_FILE:
#endif
			if (download_file_name != NULL) {
				char file_path[512];
				snprintf(file_path, sizeof(file_path), "/tmp/%s", download_file_name);
				rename(ICWMP_DOWNLOAD_FILE, file_path);
			} else {
				rename(ICWMP_DOWNLOAD_FILE, VENDOR_CONFIG_FILE);
			}

			error = FAULT_CPE_NO_FAULT;
			break;
		case DOWNLOAD_FILETYPE_TONE_FILE:
		case DOWNLOAD_FILETYPE_RINGER:
			//TODO Not Supported, ignore the request
			CWMP_LOG(ERROR, "Filetype [%s] not currently supported, removing downloaded file", pdownload->file_type);
			error = FAULT_CPE_NO_FAULT;
			remove_file(ICWMP_DOWNLOAD_FILE);
			break;
		default:
			remove_file(ICWMP_DOWNLOAD_FILE);
			error = FAULT_CPE_INVALID_ARGUMENTS;
			snprintf(err_msg, sizeof(err_msg), "Invalid file type: (%s)", pdownload->file_type);
	}

end_download:
	get_time(time(NULL), download_endTime, sizeof(download_endTime));

	p = calloc(1, sizeof(struct transfer_complete));
	if (p == NULL || ptransfer_complete == NULL) {
		CWMP_LOG(ERROR, "%s: Failed to allocate memory", __FUNCTION__);
		error = FAULT_CPE_INTERNAL_ERROR;
		return error;
	}

	p->command_key = pdownload->command_key ? strdup(pdownload->command_key) : strdup("");
	p->start_time = CWMP_STRDUP(download_startTime);
	p->complete_time = CWMP_STRDUP(download_endTime);
	p->type = ltype;
	p->file_type = CWMP_STRDUP(pdownload->file_type);
	if (error != FAULT_CPE_NO_FAULT) {
		p->fault_code = error;
	}

	p->fault_string = strdup(err_msg);
	*ptransfer_complete = p;

	return error;
}

char *get_file_name_by_download_url(char *url)
{
	if (url == NULL) {
		CWMP_LOG(ERROR, "download %s: url is null", __FUNCTION__);
		return NULL;
	}
	char *slash = strrchr(url, '/');
	if (slash == NULL)
			return NULL;
	return slash+1;
}

int apply_downloaded_file(struct download *pdownload, char *download_file_name, enum load_type ltype, struct transfer_complete *ptransfer_complete)
{
	int error = FAULT_CPE_NO_FAULT;
	char err_msg[256] = {0};
	char file_path[512] = {0};

	if (pdownload->filetype == DOWNLOAD_FILETYPE_FIRMWARE_UPGRADE_IMAGE) {
		ptransfer_complete->old_software_version = cwmp_ctx.deviceid.softwareversion;
	}
	if (ptransfer_complete->id <= 0) {
		if ((cwmp_ctx.tc_id < 0) || (cwmp_ctx.tc_id >= MAX_INT_ID)) {
			cwmp_ctx.tc_id = 0;
		}
		cwmp_ctx.tc_id++;
		ptransfer_complete->id = cwmp_ctx.tc_id;
	}
	bkp_session_insert_transfer_complete(ptransfer_complete);
	bkp_session_save();

	switch (pdownload->filetype) {
		case DOWNLOAD_FILETYPE_FIRMWARE_UPGRADE_IMAGE:
#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
		case DOWNLOAD_FILETYPE_UPGRADE_WITH_NOCONF:
		case DOWNLOAD_FILETYPE_UPGRADE_WITH_USERCONF:
		case DOWNLOAD_FILETYPE_UPGRADE_WITH_FULLCONF:
#endif
			if (cwmp_apply_multiple_firmware(pdownload->filetype, true) == 0) {
				cwmp_reboot("FirmwareUpgrade");
				if (error == FAULT_CPE_NO_FAULT) {
					sleep(70);
					error = FAULT_CPE_DOWNLOAD_FAIL_COMPLETE_DOWNLOAD;
					snprintf(err_msg, sizeof(err_msg), "Failed to reboot after upgrade");
					CWMP_LOG(ERROR, "## Failed to reboot after upgrade");
				}
			} else {
				error = FAULT_CPE_DOWNLOAD_FAIL_FILE_CORRUPTED;
				snprintf(err_msg, sizeof(err_msg), "Failed in applying the downloaded firmware image, may be corrupted file");
				CWMP_LOG(ERROR, "Failed in applying the downloaded firmware image, may be corrupted file");
			}
			break;
		case DOWNLOAD_FILETYPE_WEB_CONTENT:
			// apply web content file
			if (download_file_name != NULL) {
				snprintf(file_path, sizeof(file_path), "file:///tmp/%s", download_file_name);
			} else {
				snprintf(file_path, sizeof(file_path), "file://%s", WEB_CONTENT_FILE);
			}

			if (cwmp_apply_web_content(file_path) == false) {
				error = FAULT_CPE_DOWNLOAD_FAIL_FILE_CORRUPTED;
				snprintf(err_msg, sizeof(err_msg), "Failed in applying the downloaded web content, may be corrupted file");
			} else {
				error = FAULT_CPE_NO_FAULT;
			}

			remove_file(file_path);
			break;
		case DOWNLOAD_FILETYPE_VENDOR_CONFIG:
#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
		case DOWNLOAD_FILETYPE_OPERATOR_CONF_FILE:
#endif
			int err = CWMP_OK;
			bool is_opconf = false;

			if (CWMP_STRLEN(download_file_name) != 0) {
				snprintf(file_path, sizeof(file_path), "/tmp/%s", download_file_name);
			} else {
				snprintf(file_path, sizeof(file_path), "%s", VENDOR_CONFIG_FILE);
			}

			is_opconf = is_downloaded_file_opconf(file_path);
			if (is_opconf == false) { // handle normal uci download
				char buffer[1024] = {0};

				// First run the macro_engine.sh to resolve the macros in uci (if present)
				if (file_exists(MACRO_ENGINE) == true) {
					snprintf(buffer, sizeof(buffer), "%s %s", MACRO_ENGINE, file_path);
					run_cmd(buffer, NULL, 0);
				}

				// If the downloaded filename is one of the uci name, use named import
				snprintf(buffer, sizeof(buffer), "/etc/config/%s", download_file_name);
				if (file_exists(buffer) == true) {
					err = import_uci_package(download_file_name, file_path);
				} else {
					err = import_uci_package(NULL, file_path);
				}

				if (err == CWMP_GEN_ERR) {
					error = FAULT_CPE_INTERNAL_ERROR;
					snprintf(err_msg, sizeof(err_msg), "Failed to commit the config file changes");
				} else if (err == -1) {
					error = FAULT_CPE_DOWNLOAD_FAIL_FILE_CORRUPTED;
					snprintf(err_msg, sizeof(err_msg), "UCI operation failed, could not import config file");
				}
#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
			} else {
				err = cwmp_apply_opconf(file_path, pdownload->filetype);
				if (err != CWMP_OK) {
					snprintf(err_msg, sizeof(err_msg), "Failed to apply the opconf file");
					error = FAULT_CPE_DOWNLOAD_FAIL_FILE_CORRUPTED;
				}
#endif
			}
			remove_file(file_path);
			// Trigger reboot/soft-reset only in case of its not operator default opconf file,
			// operator will trigger factory reset to apply it latter, triggering factoryreset
			// will break the DownloadRPC message flow
			if (err == CWMP_OK) {
				if (pdownload->filetype == DOWNLOAD_FILETYPE_VENDOR_CONFIG) {
					cwmp_reboot("VendorConfigApply");
					if (error == FAULT_CPE_NO_FAULT) {
						sleep(70);
						error = FAULT_CPE_DOWNLOAD_FAIL_COMPLETE_DOWNLOAD;
						snprintf(err_msg, sizeof(err_msg), "Failed to reboot after config apply");
						CWMP_LOG(ERROR, "## Failed to reboot after config apply");
					}
				}
			}
			break;
		case DOWNLOAD_FILETYPE_TONE_FILE:
		case DOWNLOAD_FILETYPE_RINGER:
			error = FAULT_CPE_NO_FAULT;
			break;
		case DOWNLOAD_FILETYPE_STORED_FIRMWARE_IMAGE:
			error = cwmp_apply_multiple_firmware(pdownload->filetype, false);
			if (error != CWMP_OK) {
				if (error == -1)
					snprintf(err_msg, sizeof(err_msg), "Failed to get available bank id");
				else
					snprintf(err_msg, sizeof(err_msg), "Failed in fwbank upgrade ubus method");

				error = FAULT_CPE_DOWNLOAD_FAILURE;
			}
			break;
		default:
			error = FAULT_CPE_INVALID_ARGUMENTS;
			snprintf(err_msg, sizeof(err_msg), "Invalid file type argument (%s)", pdownload->file_type);
	}

	if (error == FAULT_CPE_NO_FAULT) {
		set_rpc_parameter_key(pdownload->command_key);
		if (pdownload->filetype == DOWNLOAD_FILETYPE_WEB_CONTENT ||
#ifdef ICWMP_ENABLE_OPCONF_SUPPORT
			pdownload->filetype == DOWNLOAD_FILETYPE_OPERATOR_CONF_FILE ||
#endif
			pdownload->filetype == DOWNLOAD_FILETYPE_STORED_FIRMWARE_IMAGE) {

			bkp_session_delete_element((ltype == TYPE_DOWNLOAD) ? "download" : "schedule_download", pdownload->id);
			bkp_session_delete_element_by_key("transfer_complete", "start_time", ptransfer_complete->start_time);
			bkp_session_save();
		}
		CWMP_LOG(DEBUG, "Download and apply new filetype [%d=>%s] is done successfully", pdownload->filetype, pdownload->file_type);
		return FAULT_CPE_NO_FAULT;
	}

	// In case of failure, save transfer complete failures report
	bkp_session_delete_element_by_key("transfer_complete", "start_time", ptransfer_complete->start_time);
	ptransfer_complete->fault_code = error;
	if (ptransfer_complete->id <= 0) {
		if ((cwmp_ctx.tc_id < 0) || (cwmp_ctx.tc_id >= MAX_INT_ID)) {
			cwmp_ctx.tc_id = 0;
		}
		cwmp_ctx.tc_id++;
		ptransfer_complete->id = cwmp_ctx.tc_id;
	}
	ptransfer_complete->fault_string = strdup(err_msg);

	bkp_session_insert_transfer_complete(ptransfer_complete);
	bkp_session_save();
	//cwmp_root_cause_transfer_complete(ptransfer_complete);
	return error;
}

struct transfer_complete *set_download_error_transfer_complete(struct download *pdownload, enum load_type ltype)
{
	struct transfer_complete *ptransfer_complete;
	ptransfer_complete = calloc(1, sizeof(struct transfer_complete));
	if (ptransfer_complete != NULL) {
		char start_time[26] = {0};
		get_time(time(NULL), start_time, sizeof(start_time));

		ptransfer_complete->command_key = strdup(pdownload && pdownload->command_key ? pdownload->command_key : "");
		ptransfer_complete->start_time = CWMP_STRDUP(start_time);
		ptransfer_complete->complete_time = strdup(ptransfer_complete->start_time ? ptransfer_complete->start_time  : "");
		ptransfer_complete->fault_code = ltype == TYPE_DOWNLOAD ? FAULT_CPE_DOWNLOAD_FAILURE : FAULT_CPE_DOWNLOAD_FAIL_WITHIN_TIME_WINDOW;
		ptransfer_complete->type = ltype;
		if (ptransfer_complete->id <= 0) {
			if ((cwmp_ctx.tc_id < 0) || (cwmp_ctx.tc_id >= MAX_INT_ID)) {
				cwmp_ctx.tc_id = 0;
			}
			cwmp_ctx.tc_id++;
			ptransfer_complete->id = cwmp_ctx.tc_id;
		}
		bkp_session_insert_transfer_complete(ptransfer_complete);
		cwmp_root_cause_transfer_complete(ptransfer_complete);
	}
	return ptransfer_complete;
}

int cwmp_free_download_request(struct download *download)
{
	if (download != NULL) {
		if (download->command_key != NULL)
			free(download->command_key);

		if (download->file_type != NULL)
			free(download->file_type);

		if (download->url != NULL)
			free(download->url);

		if (download->username != NULL)
			free(download->username);

		if (download->password != NULL)
			free(download->password);

		free(download);
	}
	return CWMP_OK;
}

int cwmp_free_schedule_download_request(struct download *schedule_download)
{
	if (schedule_download != NULL) {
		if (schedule_download->command_key != NULL)
			free(schedule_download->command_key);

		if (schedule_download->file_type != NULL)
			free(schedule_download->file_type);

		if (schedule_download->url != NULL)
			free(schedule_download->url);

		if (schedule_download->username != NULL)
			free(schedule_download->username);

		if (schedule_download->password != NULL)
			free(schedule_download->password);

		for (int i = 0; i <= 1; i++) {
			if (schedule_download->timewindowstruct[i].windowmode != NULL)
				free(schedule_download->timewindowstruct[i].windowmode);

			if (schedule_download->timewindowstruct[i].usermessage != NULL)
				free(schedule_download->timewindowstruct[i].usermessage);
		}
		free(schedule_download);
	}
	return CWMP_OK;
}

int cwmp_scheduledDownload_remove_all()
{
	while (list_download.next != &(list_download)) {
		struct download *download;
		download = list_entry(list_download.next, struct download, list);
		list_del(&(download->list));
		bkp_session_delete_element("schedule_download", download->id);
		if (download->scheduled_time != 0)
			count_download_queue--;
		cwmp_free_download_request(download);
	}

	return CWMP_OK;
}

int cwmp_scheduled_Download_remove_all()
{
	while (list_schedule_download.next != &(list_schedule_download)) {
		struct download *schedule_download;
		schedule_download = list_entry(list_schedule_download.next, struct download, list);
		list_del(&(schedule_download->list));
		bkp_session_delete_element("schedule_download", schedule_download->id);
		if (schedule_download->timewindowstruct[0].windowstart != 0)
			count_download_queue--;
		cwmp_free_schedule_download_request(schedule_download);
	}

	return CWMP_OK;
}

int cwmp_rpc_acs_destroy_data_transfer_complete(struct rpc *rpc)
{
	if (rpc && rpc->extra_data) {
		struct transfer_complete *p = (struct transfer_complete *)rpc->extra_data;
		bkp_session_delete_element_by_key("transfer_complete", "start_time", p->start_time);

		bkp_session_save();
		FREE(p->command_key);
		FREE(p->start_time);
		FREE(p->complete_time);
		FREE(p->file_type);
		FREE(p->fault_string);
	}
	if (rpc)
		FREE(rpc->extra_data);
	return 0;
}

void cwmp_start_download(struct uloop_timeout *timeout)
{
	struct download *pdownload;
	int error = FAULT_CPE_NO_FAULT;
	struct transfer_complete *ptransfer_complete;
	pdownload = container_of(timeout, struct download, handler_timer);

	char *download_file_name = get_file_name_by_download_url(pdownload->url);
	CWMP_LOG(INFO, "Launch download file %s, filetype [%d]", pdownload->url, pdownload->filetype);
	error = cwmp_launch_download(pdownload, download_file_name, TYPE_DOWNLOAD, &ptransfer_complete);
	sleep(3);
	if (error != FAULT_CPE_NO_FAULT) {
		CWMP_LOG(ERROR, "Error while downloading the file: %s", pdownload->url);
		if (ptransfer_complete->id <= 0) {
			if ((cwmp_ctx.tc_id < 0) || (cwmp_ctx.tc_id >= MAX_INT_ID)) {
				cwmp_ctx.tc_id = 0;
			}
			cwmp_ctx.tc_id++;
			ptransfer_complete->id = cwmp_ctx.tc_id;
		}
		bkp_session_insert_transfer_complete(ptransfer_complete);
		bkp_session_save();
		//cwmp_root_cause_transfer_complete(ptransfer_complete);
		//bkp_session_delete_transfer_complete(ptransfer_complete);
	} else {
		error = apply_downloaded_file(pdownload, download_file_name, TYPE_DOWNLOAD, ptransfer_complete);
		if (error != FAULT_CPE_NO_FAULT) {
			CWMP_LOG(ERROR, "Error while applying the downloaded file: %s", download_file_name);
		}
	}

	list_del(&(pdownload->list));
	if (pdownload->scheduled_time != 0) {
		count_download_queue--;
	}
	cwmp_free_download_request(pdownload);

	struct session_timer_event *download_inform_event = calloc(1, sizeof(struct session_timer_event));

	download_inform_event->extra_data = ptransfer_complete;
	download_inform_event->session_timer_evt.cb = cwmp_schedule_session_with_event;
	download_inform_event->event = TransferClt_Evt;
	trigger_cwmp_session_timer_with_event(&download_inform_event->session_timer_evt);
}

void apply_downloads()
{
	struct list_head *ilist;
	list_for_each (ilist, &(list_download)) {
		struct download *download = list_entry(ilist, struct download, list);
		int download_delay = 0;
		if (download->scheduled_time > time(NULL)) {
			download_delay = download->scheduled_time - time(NULL);
		}
		uloop_timeout_set(&download->handler_timer, 1000 * download_delay);
	}
}

void cwmp_start_schedule_download(struct uloop_timeout *timeout)
{
	struct download *sched_download;
	struct transfer_complete *ptransfer_complete;
	sched_download = container_of(timeout, struct download, handler_timer);
	bool outdate = false;
	int delay;
	int window_index;

	time_t now = time(NULL);
	if (sched_download->timewindowstruct[0].windowstart > now) {
		delay = sched_download->timewindowstruct[0].windowstart - now;
		uloop_timeout_set(&sched_download->handler_timer, 1000 * delay);
		return;
	} else if (sched_download->timewindowstruct[0].windowend >= now) {
		outdate = false;
		window_index = 0;
	} else if (sched_download->timewindowstruct[1].windowstart > now) {
		delay = sched_download->timewindowstruct[1].windowstart - now;
		uloop_timeout_set(&sched_download->handler_timer, 1000 * delay);
		return;
	} else if (sched_download->timewindowstruct[1].windowend >= now) {
		outdate = false;
		window_index = 1;
	} else {
		outdate = true;
	}

	if (!outdate) {
		int error;
		char *download_file_name = get_file_name_by_download_url(sched_download->url);
		CWMP_LOG(INFO, "Launch download file %s", sched_download->url);
		error = cwmp_launch_download(sched_download, download_file_name, TYPE_SCHEDULE_DOWNLOAD, &ptransfer_complete);
		sleep(3);
		if (error != FAULT_CPE_NO_FAULT) {
			CWMP_LOG(ERROR, "Error while downloading the file: %s", sched_download->url);
			goto retry;
		} else {
			error = apply_downloaded_file(sched_download, download_file_name, TYPE_SCHEDULE_DOWNLOAD, ptransfer_complete);
			if (error != FAULT_CPE_NO_FAULT) {
				CWMP_LOG(ERROR, "[%s:%d]Error while applying the downloaded file: %s", __func__, __LINE__, download_file_name);
				goto retry;
			}
		}
	} else {
		CWMP_LOG(ERROR, "Schedule Download out of date");
		ptransfer_complete = calloc(1, sizeof(struct transfer_complete));
		if (ptransfer_complete == NULL) {
			// error = FAULT_CPE_INTERNAL_ERROR;
			return;
		}

		char cur_time[26] = {0};
		get_time(now, cur_time, sizeof(cur_time));

		ptransfer_complete->command_key = sched_download->command_key ? strdup(sched_download->command_key) : strdup("");
		ptransfer_complete->start_time = CWMP_STRDUP(cur_time);
		ptransfer_complete->complete_time = CWMP_STRDUP(cur_time);
		ptransfer_complete->type = TYPE_DOWNLOAD;
		ptransfer_complete->fault_code = FAULT_CPE_INTERNAL_ERROR;
		if (ptransfer_complete->id <= 0) {
			if ((cwmp_ctx.tc_id < 0) || (cwmp_ctx.tc_id >= MAX_INT_ID)) {
				cwmp_ctx.tc_id = 0;
			}
			cwmp_ctx.tc_id++;
			ptransfer_complete->id = cwmp_ctx.tc_id;
		}
		bkp_session_insert_transfer_complete(ptransfer_complete);
		bkp_session_save();
		//cwmp_root_cause_transfer_complete(ptransfer_complete);
		bkp_session_delete_element_by_key("transfer_complete", "start_time", ptransfer_complete->start_time);
	}

	return;

retry:
	if (sched_download->timewindowstruct[window_index].maxretries > 0) {
		uloop_timeout_set(&sched_download->handler_timer, 10);
		sched_download->timewindowstruct[window_index].maxretries--;
		return;
	} else {
		if (ptransfer_complete->id <= 0) {
			if ((cwmp_ctx.tc_id < 0) || (cwmp_ctx.tc_id >= MAX_INT_ID)) {
				cwmp_ctx.tc_id = 0;
			}
			cwmp_ctx.tc_id++;
			ptransfer_complete->id = cwmp_ctx.tc_id;
		}
		bkp_session_insert_transfer_complete(ptransfer_complete);
		bkp_session_save();
		//cwmp_root_cause_transfer_complete(ptransfer_complete);
		bkp_session_delete_element_by_key("transfer_complete", "start_time", ptransfer_complete->start_time);
		bkp_session_save();
	}
	list_del(&(sched_download->list));
	if (sched_download->scheduled_time != 0)
		count_download_queue--;
	cwmp_free_schedule_download_request(sched_download);

	struct session_timer_event *sched_download_inform_event = calloc(1, sizeof(struct session_timer_event));

	sched_download_inform_event->extra_data = ptransfer_complete;
	sched_download_inform_event->session_timer_evt.cb = cwmp_schedule_session_with_event;
	sched_download_inform_event->event = TransferClt_Evt;
	trigger_cwmp_session_timer_with_event(&sched_download_inform_event->session_timer_evt);
}

void apply_schedule_downloads()
{
	struct list_head *ilist;
	list_for_each (ilist, &(list_schedule_download)) {
		struct download *sched_download = list_entry(ilist, struct download, list);
		time_t now = time(NULL);
		int download_delay;
		if (sched_download->timewindowstruct[0].windowstart > now)
			download_delay = sched_download->timewindowstruct[0].windowstart - now;
		else if (sched_download->timewindowstruct[0].windowend >= now)
			download_delay = 1;
		else if (now < sched_download->timewindowstruct[1].windowstart)
			download_delay = sched_download->timewindowstruct[1].windowstart - now;
		else if (sched_download->timewindowstruct[1].windowend >= now)
			download_delay = 1;
		else
			download_delay = 1;

		uloop_timeout_set(&sched_download->handler_timer, 1000 * download_delay);
	}
}

enum download_filetypes get_download_filetype(const char *file_type)
{
	int i;
	enum download_filetypes filetype = __MAX_DOWNLOAD_FILETYPE;

	if (CWMP_STRLEN(file_type) == 0) {
		CWMP_LOG(WARNING, "empty file_type, can't process");
		return __MAX_DOWNLOAD_FILETYPE;
	}

	for (i = DOWNLOAD_FILETYPE_FIRMWARE_UPGRADE_IMAGE; i < __MAX_DOWNLOAD_FILETYPE; i++) {
		if (CWMP_STRCMP(g_download_file_types[i], file_type) == 0) {
			filetype = i;
			break;
		}
	}

	return filetype;
}
