/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

#include "trapper_item_test.h"
#include "zbxexpression.h"

#include "zbxtrapper.h"
#include "zbxcacheconfig.h"
#include "zbxdbhigh.h"
#include "zbxdbschema.h"
#include "zbxeval.h"
#include "zbxjson.h"
#include "zbxstr.h"
#include "zbxpoller.h"
#include "zbxtasks.h"
#include "zbxcommshigh.h"
#include "zbxversion.h"
#ifdef HAVE_OPENIPMI
#include "zbxipmi.h"
#endif
#include "zbxnum.h"
#include "zbxsysinfo.h"
#include "zbx_item_constants.h"

static void	dump_item(const zbx_dc_item_t *item)
{
	if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_TRACE))
	{
		zbx_log_handle(LOG_LEVEL_TRACE, "key:'%s'", item->key);
		zbx_log_handle(LOG_LEVEL_TRACE, "  type: %u", item->type);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmp_version: %u", item->snmp_version);
		zbx_log_handle(LOG_LEVEL_TRACE, "  value_type: %u", item->value_type);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmpv3_securitylevel: %u", item->snmpv3_securitylevel);
		zbx_log_handle(LOG_LEVEL_TRACE, "  authtype: %u", item->authtype);
		zbx_log_handle(LOG_LEVEL_TRACE, "  flags: %u", item->flags);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmpv3_authprotocol: %u", item->snmpv3_authprotocol);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmpv3_privprotocol: %u", item->snmpv3_privprotocol);
		zbx_log_handle(LOG_LEVEL_TRACE, "  follow_redirects: %u", item->follow_redirects);
		zbx_log_handle(LOG_LEVEL_TRACE, "  post_type: %u", item->post_type);
		zbx_log_handle(LOG_LEVEL_TRACE, "  retrieve_mode: %u", item->retrieve_mode);
		zbx_log_handle(LOG_LEVEL_TRACE, "  request_method: %u", item->request_method);
		zbx_log_handle(LOG_LEVEL_TRACE, "  output_format: %u", item->output_format);
		zbx_log_handle(LOG_LEVEL_TRACE, "  verify_peer: %u", item->verify_peer);
		zbx_log_handle(LOG_LEVEL_TRACE, "  verify_host: %u", item->verify_host);
		zbx_log_handle(LOG_LEVEL_TRACE, "  ipmi_sensor:'%s'", item->ipmi_sensor);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmp_community:'%s'", item->snmp_community);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmp_oid:'%s'", item->snmp_oid);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmpv3_securityname:'%s'", item->snmpv3_securityname);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmpv3_authpassphrase:'%s'", item->snmpv3_authpassphrase);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmpv3_privpassphrase:'%s'", item->snmpv3_privpassphrase);
		zbx_log_handle(LOG_LEVEL_TRACE, "  params:'%s'", ZBX_NULL2STR(item->params));
		zbx_log_handle(LOG_LEVEL_TRACE, "  username:'%s'", item->username);
		zbx_log_handle(LOG_LEVEL_TRACE, "  publickey:'%s'", item->publickey);
		zbx_log_handle(LOG_LEVEL_TRACE, "  privatekey:'%s'", item->privatekey);
		zbx_log_handle(LOG_LEVEL_TRACE, "  password:'%s'", item->password);
		zbx_log_handle(LOG_LEVEL_TRACE, "  snmpv3_contextname:'%s'", item->snmpv3_contextname);
		zbx_log_handle(LOG_LEVEL_TRACE, "  jmx_endpoint:'%s'", item->jmx_endpoint);
		zbx_log_handle(LOG_LEVEL_TRACE, "  timeout: %d", item->timeout);
		zbx_log_handle(LOG_LEVEL_TRACE, "  url:'%s'", item->url);
		zbx_log_handle(LOG_LEVEL_TRACE, "  query_fields:'%s'", item->query_fields);
		zbx_log_handle(LOG_LEVEL_TRACE, "  posts:'%s'", ZBX_NULL2STR(item->posts));
		zbx_log_handle(LOG_LEVEL_TRACE, "  status_codes:'%s'", item->status_codes);
		zbx_log_handle(LOG_LEVEL_TRACE, "  http_proxy:'%s'", item->http_proxy);
		zbx_log_handle(LOG_LEVEL_TRACE, "  headers:'%s'", ZBX_NULL2STR(item->headers));
		zbx_log_handle(LOG_LEVEL_TRACE, "  ssl_cert_file:'%s'", item->ssl_cert_file);
		zbx_log_handle(LOG_LEVEL_TRACE, "  ssl_key_file:'%s'", item->ssl_key_file);
		zbx_log_handle(LOG_LEVEL_TRACE, "  ssl_key_password:'%s'", item->ssl_key_password);
		zbx_log_handle(LOG_LEVEL_TRACE, "interfaceid: " ZBX_FS_UI64, item->interface.interfaceid);
		zbx_log_handle(LOG_LEVEL_TRACE, "  useip: %u", item->interface.useip);
		zbx_log_handle(LOG_LEVEL_TRACE, "  address:'%s'", ZBX_NULL2STR(item->interface.addr));
		zbx_log_handle(LOG_LEVEL_TRACE, "  port: %u", item->interface.port);
		zbx_log_handle(LOG_LEVEL_TRACE, "hostid: " ZBX_FS_UI64, item->host.hostid);
		zbx_log_handle(LOG_LEVEL_TRACE, "  proxyid: " ZBX_FS_UI64, item->host.proxyid);
		zbx_log_handle(LOG_LEVEL_TRACE, "  host:'%s'", item->host.host);
		zbx_log_handle(LOG_LEVEL_TRACE, "  maintenance_status: %u", item->host.maintenance_status);
		zbx_log_handle(LOG_LEVEL_TRACE, "  maintenance_type: %u", item->host.maintenance_type);
		zbx_log_handle(LOG_LEVEL_TRACE, "  available: %u", item->interface.available);
		zbx_log_handle(LOG_LEVEL_TRACE, "  ipmi_authtype: %d", item->host.ipmi_authtype);
		zbx_log_handle(LOG_LEVEL_TRACE, "  ipmi_privilege: %u", item->host.ipmi_privilege);
		zbx_log_handle(LOG_LEVEL_TRACE, "  ipmi_username:'%s'", item->host.ipmi_username);
		zbx_log_handle(LOG_LEVEL_TRACE, "  ipmi_password:'%s'", item->host.ipmi_password);
		zbx_log_handle(LOG_LEVEL_TRACE, "  tls_connect: %u", item->host.tls_connect);
	#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
		zbx_log_handle(LOG_LEVEL_TRACE, "  tls_issuer:'%s'", item->host.tls_issuer);
		zbx_log_handle(LOG_LEVEL_TRACE, "  tls_subject:'%s'", item->host.tls_subject);
		zbx_log_handle(LOG_LEVEL_TRACE, "  tls_psk_identity:'%s'", item->host.tls_psk_identity);
		zbx_log_handle(LOG_LEVEL_TRACE, "  tls_psk:'%s'", item->host.tls_psk);
	#endif
	}
}

static char	*db_string_from_json_dyn(const struct zbx_json_parse *jp, const char *name, const zbx_db_table_t *table,
		const char *fieldname)
{
	char	*string = NULL;
	size_t	size = 0;

	if (SUCCEED == zbx_json_value_by_name_dyn(jp, name, &string, &size, NULL))
		return string;

	return zbx_strdup(NULL, zbx_db_get_field(table, fieldname)->default_value);
}

static void	db_string_from_json(const struct zbx_json_parse *jp, const char *name, const zbx_db_table_t *table,
		const char *fieldname, char *string, size_t len)
{

	if (SUCCEED != zbx_json_value_by_name(jp, name, string, len, NULL))
		zbx_strlcpy(string, zbx_db_get_field(table, fieldname)->default_value, len);
}

static void	db_uchar_from_json(const struct zbx_json_parse *jp, const char *name, const zbx_db_table_t *table,
		const char *fieldname, unsigned char *string)
{
	char	tmp[ZBX_MAX_UINT64_LEN + 1];

	if (SUCCEED == zbx_json_value_by_name(jp, name, tmp, sizeof(tmp), NULL))
		ZBX_STR2UCHAR(*string, tmp);
	else
		ZBX_STR2UCHAR(*string, zbx_db_get_field(table, fieldname)->default_value);
}

static void	db_int_from_json(const struct zbx_json_parse *jp, const char *name, const zbx_db_table_t *table,
		const char *fieldname, int *num)
{
	char	tmp[ZBX_MAX_UINT64_LEN + 1];

	if (SUCCEED == zbx_json_value_by_name(jp, name, tmp, sizeof(tmp), NULL))
		*num = atoi(tmp);
	else
		*num = atoi(zbx_db_get_field(table, fieldname)->default_value);
}

int	zbx_trapper_item_test_run(const struct zbx_json_parse *jp_data, zbx_uint64_t proxyid, char **info,
		const zbx_config_comms_args_t *config_comms, int config_startup_time, unsigned char program_type,
		const char *progname, zbx_get_config_forks_f get_config_forks,  const char *config_java_gateway,
		int config_java_gateway_port, const char *config_externalscripts,
		zbx_get_value_internal_ext_f get_value_internal_ext_cb, const char *config_ssh_key_location,
		const char *config_webdriver_url)
{
	char				tmp[MAX_STRING_LEN + 1], **pvalue;
	zbx_dc_item_t			item;
	static const zbx_db_table_t	*table_items, *table_interface, *table_interface_snmp, *table_hosts;
	struct zbx_json_parse		jp_item, jp_host, jp_steps, jp_interface, jp_details, jp_script_params;
	AGENT_RESULT			result;
	int				errcode, ret = FAIL;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	memset(&item, 0, sizeof(item));

	// item JSON object presence is checked in the calling function
	if (FAIL == zbx_json_brackets_by_name(jp_data, ZBX_PROTO_TAG_ITEM, &jp_item))
	{
		char	*jp_data_contents = NULL;
		size_t	offset = 0, alloc = 0;

		zbx_strncpy_alloc(&jp_data_contents, &alloc, &offset,
				jp_data->start, (size_t)(jp_data->end - jp_data->start + 1));
		zabbix_log(LOG_LEVEL_WARNING, "unexpected absence of %s tag in item.test data: %s", ZBX_PROTO_TAG_ITEM,
				jp_data_contents);
		zbx_free(jp_data_contents);
		THIS_SHOULD_NEVER_HAPPEN;

		goto out;
	}

	if (FAIL == zbx_json_brackets_by_name(jp_data, ZBX_PROTO_TAG_HOST, &jp_host))
		zbx_json_open("{}", &jp_host);

	if (FAIL == zbx_json_brackets_by_name(&jp_item, ZBX_PROTO_TAG_STEPS, &jp_steps))
		jp_steps.end = jp_steps.start = NULL;

	if (NULL == table_items)
		table_items = zbx_db_get_table("items");

	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_TYPE, table_items, "type", &item.type);
	db_string_from_json(&jp_item, ZBX_PROTO_TAG_KEY, table_items, "key_", item.key_orig, sizeof(item.key_orig));
	item.key = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_KEY, table_items, "key_");

	if (0 != proxyid && FAIL == zbx_is_item_processed_by_server(item.type, item.key))
	{
		struct zbx_json	json;

		zbx_json_init_with(&json, jp_data->start, (size_t)(jp_data->end - jp_data->start + 1));
		zbx_json_addint64(&json, ZBX_PROTO_TAG_PREPROC, 1);
		ret = zbx_tm_execute_task_data(json.buffer, json.buffer_size, proxyid, info);
		zbx_json_free(&json);

		goto out;
	}

	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_VALUE_TYPE, table_items, "value_type", &item.value_type);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_AUTHTYPE, table_items, "authtype", &item.authtype);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_FLAGS, table_items, "flags", &item.flags);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_FOLLOW_REDIRECTS, table_items, "follow_redirects",
			&item.follow_redirects);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_POST_TYPE, table_items, "post_type", &item.post_type);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_RETRIEVE_MODE, table_items, "retrieve_mode", &item.retrieve_mode);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_REQUEST_METHOD, table_items, "request_method", &item.request_method);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_OUTPUT_FORMAT, table_items, "output_format", &item.output_format);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_VERIFY_PEER, table_items, "verify_peer", &item.verify_peer);
	db_uchar_from_json(&jp_item, ZBX_PROTO_TAG_VERIFY_HOST, table_items, "verify_host", &item.verify_host);

	db_string_from_json(&jp_item, ZBX_PROTO_TAG_IPMI_SENSOR, table_items, "ipmi_sensor", item.ipmi_sensor,
			sizeof(item.ipmi_sensor));

	item.snmp_oid = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_SNMP_OID, table_items, "snmp_oid");
	item.params = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_PARAMS, table_items, "params");
	item.username = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_USERNAME, table_items, "username");
	item.publickey = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_PUBLICKEY, table_items, "publickey");
	item.privatekey = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_PRIVATEKEY, table_items, "privatekey");
	item.password = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_PASSWORD, table_items, "password");
	item.jmx_endpoint = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_JMX_ENDPOINT, table_items, "jmx_endpoint");

	switch (item.type)
	{
		case ITEM_TYPE_ZABBIX:
		case ITEM_TYPE_ZABBIX_ACTIVE:
		case ITEM_TYPE_SIMPLE:
		case ITEM_TYPE_EXTERNAL:
		case ITEM_TYPE_DB_MONITOR:
		case ITEM_TYPE_SSH:
		case ITEM_TYPE_TELNET:
		case ITEM_TYPE_SNMP:
		case ITEM_TYPE_SCRIPT:
		case ITEM_TYPE_BROWSER:
		case ITEM_TYPE_HTTPAGENT:
			db_string_from_json(&jp_item, ZBX_PROTO_TAG_TIMEOUT, table_items, "timeout", item.timeout_orig,
					sizeof(item.timeout_orig));
			break;
	}

	item.url = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_URL, table_items, "url");
	item.query_fields = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_QUERY_FIELDS, table_items, "query_fields");

	item.posts = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_POSTS, table_items, "posts");

	item.status_codes = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_STATUS_CODES, table_items, "status_codes");
	item.http_proxy = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_HTTP_PROXY, table_items, "http_proxy");

	item.headers = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_HTTP_HEADERS, table_items, "headers");

	item.ssl_cert_file = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_SSL_CERT_FILE, table_items,
			"ssl_cert_file");
	item.ssl_key_file = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_SSL_KEY_FILE, table_items, "ssl_key_file");
	item.ssl_key_password = db_string_from_json_dyn(&jp_item, ZBX_PROTO_TAG_SSL_KEY_PASSWORD, table_items,
			"ssl_key_password");

	zbx_vector_ptr_pair_create(&item.script_params);
	if ((ITEM_TYPE_SCRIPT == item.type || ITEM_TYPE_BROWSER == item.type) &&
			SUCCEED == zbx_json_brackets_by_name(&jp_item, ZBX_PROTO_TAG_PARAMETERS, &jp_script_params))
	{
#define	MAX_PARAM_NAME	255
		const char	*pnext = NULL;
		char		name[MAX_PARAM_NAME], value[MAX_STRING_LEN];

		while (NULL != (pnext = zbx_json_pair_next(&jp_script_params, pnext, name, sizeof(name))))
		{
			if (NULL != zbx_json_decodevalue(pnext, value, sizeof(value), NULL))
			{
				zbx_ptr_pair_t	pair;

				pair.first = zbx_strdup(NULL, name);
				pair.second = zbx_strdup(NULL, value);
				zbx_vector_ptr_pair_append(&item.script_params, pair);
			}
		}
#undef	MAX_PARAM_NAME
	}

	if (NULL == table_interface)
		table_interface = zbx_db_get_table("interface");

	if (FAIL == zbx_json_brackets_by_name(&jp_host, ZBX_PROTO_TAG_INTERFACE, &jp_interface))
		zbx_json_open("{}", &jp_interface);

	if (SUCCEED == zbx_json_value_by_name(&jp_interface, ZBX_PROTO_TAG_INTERFACE_ID, tmp, sizeof(tmp), NULL))
		ZBX_STR2UINT64(item.interface.interfaceid, tmp);
	else
		item.interface.interfaceid = 0;

	item.interface.version = ZBX_COMPONENT_VERSION(7, 0, 0);

	db_uchar_from_json(&jp_interface, ZBX_PROTO_TAG_USEIP, table_interface, "useip", &item.interface.useip);

	if (1 == item.interface.useip)
	{
		db_string_from_json(&jp_interface, ZBX_PROTO_TAG_ADDRESS, table_interface, "ip", item.interface.ip_orig,
				sizeof(item.interface.ip_orig));
		item.interface.addr = item.interface.ip_orig;
	}
	else
	{
		db_string_from_json(&jp_interface, ZBX_PROTO_TAG_ADDRESS, table_interface, "dns",
				item.interface.dns_orig, sizeof(item.interface.dns_orig));
		item.interface.addr = item.interface.dns_orig;
	}

	db_string_from_json(&jp_interface, ZBX_PROTO_TAG_PORT, table_interface, "port", item.interface.port_orig,
			sizeof(item.interface.port_orig));

	db_uchar_from_json(&jp_interface, ZBX_PROTO_TAG_AVAILABLE, table_interface, "available",
			&item.interface.available);

	if (FAIL == zbx_json_brackets_by_name(&jp_interface, ZBX_PROTO_TAG_DETAILS, &jp_details))
		zbx_json_open("{}", &jp_details);

	if (NULL == table_interface_snmp)
		table_interface_snmp = zbx_db_get_table("interface_snmp");

	db_uchar_from_json(&jp_details, ZBX_PROTO_TAG_VERSION, table_interface_snmp, "version", &item.snmp_version);
	item.snmp_community = db_string_from_json_dyn(&jp_details, ZBX_PROTO_TAG_COMMUNITY, table_interface_snmp,
			"community");
	item.snmpv3_securityname = db_string_from_json_dyn(&jp_details, ZBX_PROTO_TAG_SECURITYNAME,
			table_interface_snmp, "securityname");

	db_uchar_from_json(&jp_details, ZBX_PROTO_TAG_SECURITYLEVEL, table_interface_snmp, "securitylevel",
			&item.snmpv3_securitylevel);

	item.snmpv3_authpassphrase = db_string_from_json_dyn(&jp_details, ZBX_PROTO_TAG_AUTHPASSPHRASE,
			table_interface_snmp, "authpassphrase");
	item.snmpv3_privpassphrase = db_string_from_json_dyn(&jp_details, ZBX_PROTO_TAG_PRIVPASSPHRASE,
			table_interface_snmp, "privpassphrase");

	db_int_from_json(&jp_details, ZBX_PROTO_TAG_MAX_REPS, table_interface_snmp, "max_repetitions",
			&item.snmp_max_repetitions);

	db_uchar_from_json(&jp_details, ZBX_PROTO_TAG_AUTHPROTOCOL, table_interface_snmp, "authprotocol",
			&item.snmpv3_authprotocol);
	db_uchar_from_json(&jp_details, ZBX_PROTO_TAG_PRIVPROTOCOL, table_interface_snmp, "privprotocol",
			&item.snmpv3_privprotocol);

	item.snmpv3_contextname = db_string_from_json_dyn(&jp_details, ZBX_PROTO_TAG_CONTEXTNAME, table_interface_snmp,
			"contextname");

	if (NULL == table_hosts)
		table_hosts = zbx_db_get_table("hosts");

	db_string_from_json(&jp_host, ZBX_PROTO_TAG_HOST, table_hosts, "host", item.host.host, sizeof(item.host.host));

	if (SUCCEED == zbx_json_value_by_name(&jp_host, ZBX_PROTO_TAG_HOSTID, tmp, sizeof(tmp), NULL))
		ZBX_STR2UINT64(item.host.hostid, tmp);
	else
		item.host.hostid = 0;

	db_uchar_from_json(&jp_host, ZBX_PROTO_TAG_MAINTENANCE_STATUS, table_hosts, "maintenance_status",
			&item.host.maintenance_status);
	db_uchar_from_json(&jp_host, ZBX_PROTO_TAG_MAINTENANCE_TYPE, table_hosts, "maintenance_type",
			&item.host.maintenance_type);

	if (SUCCEED == zbx_json_value_by_name(&jp_host, ZBX_PROTO_TAG_IPMI_AUTHTYPE, tmp, sizeof(tmp), NULL))
	{
		item.host.ipmi_authtype = (signed char)atoi(tmp);
	}
	else
	{
		item.host.ipmi_authtype =
				(signed char)atoi(zbx_db_get_field(table_hosts, "ipmi_authtype")->default_value);
	}

	db_uchar_from_json(&jp_host, ZBX_PROTO_TAG_IPMI_PRIVILEGE, table_hosts, "ipmi_privilege",
			&item.host.ipmi_privilege);
	db_string_from_json(&jp_host, ZBX_PROTO_TAG_IPMI_USERNAME, table_hosts, "ipmi_username",
			item.host.ipmi_username, sizeof(item.host.ipmi_username));
	db_string_from_json(&jp_host, ZBX_PROTO_TAG_IPMI_PASSWORD, table_hosts, "ipmi_password",
			item.host.ipmi_password, sizeof(item.host.ipmi_password));
	db_uchar_from_json(&jp_host, ZBX_PROTO_TAG_TLS_CONNECT, table_hosts, "tls_connect", &item.host.tls_connect);
#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
	db_string_from_json(&jp_host, ZBX_PROTO_TAG_TLS_ISSUER, table_hosts, "tls_issuer", item.host.tls_issuer,
			sizeof(item.host.tls_issuer));
	db_string_from_json(&jp_host, ZBX_PROTO_TAG_TLS_SUBJECT, table_hosts, "tls_subject", item.host.tls_subject,
			sizeof(item.host.tls_subject));
	db_string_from_json(&jp_host, ZBX_PROTO_TAG_TLS_PSK_IDENTITY, table_hosts, "tls_psk_identity",
			item.host.tls_psk_identity, sizeof(item.host.tls_psk_identity));
	db_string_from_json(&jp_host, ZBX_PROTO_TAG_TLS_PSK, table_hosts, "tls_psk", item.host.tls_psk,
			sizeof(item.host.tls_psk));
#endif

	if (ITEM_TYPE_IPMI == item.type)
	{
		zbx_init_agent_result(&result);

		if (FAIL == zbx_is_ushort(item.interface.port_orig, &item.interface.port))
		{
			*info = zbx_dsprintf(NULL, "Invalid port number [%s]", item.interface.port_orig);
		}
		else
		{
#ifdef HAVE_OPENIPMI
			if (0 == get_config_forks(ZBX_PROCESS_TYPE_IPMIPOLLER))
			{
				*info = zbx_strdup(NULL, "Cannot perform IPMI request: configuration parameter"
						" \"StartIPMIPollers\" is 0.");
			}
			else
				ret = zbx_ipmi_test_item(&item, info);
#else
			ZBX_UNUSED(get_config_forks);
			*info = zbx_strdup(NULL, "Support for IPMI was not compiled in.");
#endif
		}

		if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_TRACE))
			dump_item(&item);
	}
	else
	{
		zbx_vector_agent_result_ptr_t	add_results;

		zbx_vector_agent_result_ptr_create(&add_results);

		zbx_prepare_items(&item, &errcode, 1, &result, ZBX_MACRO_EXPAND_NO);

		if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_TRACE))
			dump_item(&item);

		if (ITEM_TYPE_CALCULATED == item.type)
		{
			zbx_eval_context_t	ctx;
			char			*error = NULL;

			if (FAIL == zbx_eval_parse_expression(&ctx, item.params, ZBX_EVAL_PARSE_CALC_EXPRESSION,
					&error))
			{
				zbx_eval_set_exception(&ctx, zbx_dsprintf(NULL, "Cannot parse formula: %s", error));
				zbx_free(error);
			}

			zbx_eval_serialize(&ctx, NULL, &item.formula_bin);
			zbx_eval_clear(&ctx);
		}

		zbx_check_items(&item, &errcode, 1, &result, &add_results, ZBX_NO_POLLER, config_comms,
				config_startup_time, program_type, progname, get_config_forks, config_java_gateway,
				config_java_gateway_port, config_externalscripts, get_value_internal_ext_cb,
				config_ssh_key_location, config_webdriver_url);

		switch (errcode)
		{
			case SUCCEED:
				if (NULL == (pvalue = ZBX_GET_TEXT_RESULT(&result)))
				{
					*info = zbx_strdup(NULL, "No value");
				}
				else
				{
					*info = zbx_strdup(NULL, *pvalue);
					ret = SUCCEED;
				}
				break;
			default:
				if (NULL == (pvalue = ZBX_GET_MSG_RESULT(&result)))
					*info = zbx_dsprintf(NULL, "unknown error with code %d", errcode);
				else
					*info = zbx_strdup(NULL, *pvalue);
		}

		zbx_vector_agent_result_ptr_clear_ext(&add_results, zbx_free_agent_result_ptr);
		zbx_vector_agent_result_ptr_destroy(&add_results);
	}

	zbx_clean_items(&item, 1, &result);
out:
	zbx_free(item.key);
	zbx_free(item.snmp_oid);
	zbx_free(item.params);
	zbx_free(item.username);
	zbx_free(item.publickey);
	zbx_free(item.privatekey);
	zbx_free(item.password);
	zbx_free(item.jmx_endpoint);
	zbx_free(item.url);
	zbx_free(item.query_fields);
	zbx_free(item.posts);
	zbx_free(item.status_codes);
	zbx_free(item.http_proxy);
	zbx_free(item.headers);
	zbx_free(item.ssl_cert_file);
	zbx_free(item.ssl_key_file);
	zbx_free(item.ssl_key_password);
	zbx_free(item.snmp_community);
	zbx_free(item.snmpv3_securityname);
	zbx_free(item.snmpv3_authpassphrase);
	zbx_free(item.snmpv3_privpassphrase);
	zbx_free(item.snmpv3_contextname);
	for (int i = 0; i < item.script_params.values_num; i++)
	{
		zbx_free(item.script_params.values[i].first);
		zbx_free(item.script_params.values[i].second);
	}
	zbx_vector_ptr_pair_destroy(&item.script_params);
	zbx_free(item.formula_bin);

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));

	return ret;
}

static int	try_find_preproc_value(const struct zbx_json_parse *jp_first, const char *name_first,
		const struct zbx_json_parse *jp_second, const char *name_second, char **value, size_t *value_size)
{
	int	ret = zbx_json_value_by_name_dyn(jp_first, name_first, value, value_size, NULL);

	if (FAIL == ret)
		ret = zbx_json_value_by_name_dyn(jp_second, name_second, value, value_size, NULL);

	return ret;
}

void	zbx_trapper_item_test_add_value(struct zbx_json *json, int ret, const char *info)
{
	size_t	original_size;

	zbx_json_addobject(json, ZBX_PROTO_TAG_ITEM);
	original_size = zbx_json_addstring_limit(json,
			SUCCEED == ret ? ZBX_PROTO_TAG_RESULT : ZBX_PROTO_TAG_ERROR, info, ZBX_JSON_TYPE_STRING,
			ZBX_JSON_TEST_DATA_MAX_SIZE);

	if (ZBX_JSON_TEST_DATA_MAX_SIZE < original_size)
	{
		zbx_json_addstring(json, ZBX_PROTO_TAG_TRUNCATED, "true", ZBX_JSON_TYPE_TRUE);
		zbx_json_adduint64(json, ZBX_PROTO_TAG_ORIGINAL_SIZE, original_size);
	}

	zbx_json_addstring(json, ZBX_PROTO_TAG_EOL, NULL != strstr(info, "\r\n") ? "CRLF" : "LF",
			ZBX_JSON_TYPE_STRING);

	zbx_json_close(json);
}

static int	trapper_item_test(const struct zbx_json_parse *jp, const zbx_config_comms_args_t *config_comms,
		int config_startup_time, unsigned char program_type, const char *progname,
		zbx_get_config_forks_f get_config_forks, const char *config_java_gateway, int config_java_gateway_port,
		const char *config_externalscripts, zbx_get_value_internal_ext_f get_value_internal_ext_cb,
		const char *config_ssh_key_location, const char *config_webdriver_url, struct zbx_json *json,
		char **error)
{
	zbx_user_t		user;
	struct zbx_json_parse	jp_data, jp_item, jp_host, jp_options, jp_steps;
	char			tmp[MAX_ID_LEN + 1], *info = NULL, *value = NULL, buffer[MAX_STRING_LEN], *key = NULL;
	zbx_uint64_t		proxyid = 0;
	int			ret = FAIL, state = 0, value_found;
	size_t			value_size = 0, key_size = 0;

	zbx_user_init(&user);

	if (FAIL == zbx_get_user_from_json(jp, &user, NULL) || USER_TYPE_ZABBIX_ADMIN > user.type)
	{
		*error = zbx_strdup(NULL, "Permission denied.");
		goto out;
	}

	if (SUCCEED != zbx_json_brackets_by_name(jp, ZBX_PROTO_TAG_DATA, &jp_data))
	{
		*error = zbx_dsprintf(NULL, "Cannot parse request tag: %s.", ZBX_PROTO_TAG_DATA);
		goto out;
	}

	if (FAIL == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_ITEM, &jp_item))
	{
		*error = zbx_strdup(NULL, "Missing item field.");
		goto out;
	}

	if (FAIL == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_HOST, &jp_host))
		zbx_json_open("{}", &jp_host);

	if (FAIL == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_OPTIONS, &jp_options))
		zbx_json_open("{}", &jp_options);

	if (FAIL == zbx_json_brackets_by_name(&jp_item, ZBX_PROTO_TAG_STEPS, &jp_steps))
		jp_steps.end = jp_steps.start = NULL;

	if (SUCCEED == zbx_json_value_by_name(&jp_options, ZBX_PROTO_TAG_STATE, buffer, sizeof(buffer), NULL))
		state = atoi(buffer);

	if (ITEM_STATE_NOTSUPPORTED == state)
	{
		value_found = try_find_preproc_value(&jp_options, ZBX_PROTO_TAG_RUNTIME_ERROR, &jp_item,
				ZBX_PROTO_TAG_VALUE, &value, &value_size);
	}
	else
	{
		value_found = try_find_preproc_value(&jp_item, ZBX_PROTO_TAG_VALUE, &jp_options,
				ZBX_PROTO_TAG_RUNTIME_ERROR, &value, &value_size);
	}

	if (FAIL == value_found)
	{
		// Get value from host is not checked, yet no value was provided
		if (FAIL == zbx_json_value_by_name_dyn(&jp_item, ZBX_PROTO_TAG_KEY, &key, &key_size, NULL))
		{
			if (FAIL == zbx_json_value_by_name_dyn(&jp_item, ZBX_PROTO_TAG_SNMP_OID, &key, &key_size, NULL))
			{
				*error = zbx_strdup(NULL, "Value was not provided for the preprocessing test.");
				goto out;
			}
		}

		zbx_free(key);
	}
	else
		goto preproc_test;

	if (SUCCEED == zbx_json_value_by_name(&jp_host, ZBX_PROTO_TAG_PROXYID, tmp, sizeof(tmp), NULL))
		ZBX_STR2UINT64(proxyid, tmp);

	ret = zbx_trapper_item_test_run(&jp_data, proxyid, &info, config_comms, config_startup_time, program_type,
			progname, get_config_forks, config_java_gateway, config_java_gateway_port,
			config_externalscripts, get_value_internal_ext_cb, config_ssh_key_location,
			config_webdriver_url);

	if (FAIL == ret)
		state = ITEM_STATE_NOTSUPPORTED;

	zabbix_log(LOG_LEVEL_DEBUG, "%s() json.buffer:'%s'", __func__, json->buffer);

preproc_test:
	zbx_json_addstring(json, ZBX_PROTO_TAG_RESPONSE, "success", ZBX_JSON_TYPE_STRING);

	if (0 != proxyid && zbx_dc_get_proxy_version(proxyid) >= ZBX_COMPONENT_VERSION(7, 0, 7))
	{
		struct zbx_json_parse	jp_result;

		if (SUCCEED == (ret = zbx_json_open(info, &jp_result)))
		{
			zbx_json_addraw(json, ZBX_PROTO_TAG_DATA, info);
		}
		else
		{
			*error = info;
			info = NULL;
		}
	}
	else
	{
		zbx_json_addobject(json, ZBX_PROTO_TAG_DATA);

		if (NULL != info)
		{
			zbx_trapper_item_test_add_value(json, ret, info);

			value = zbx_strdup(NULL, info);
			value_size = strlen(value);
		}

		zbx_json_addobject(json, ZBX_PROTO_TAG_PREPROCESSING);
		ret = zbx_trapper_preproc_test_run(&jp_item, &jp_options, &jp_steps, value, value_size, state, json,
				error);
	}
	zbx_json_close(json);
out:
	zbx_free(value);
	zbx_free(info);
	zbx_user_free(&user);

	return ret;
}

void	zbx_trapper_item_test(zbx_socket_t *sock, const struct zbx_json_parse *jp,
		const zbx_config_comms_args_t *config_comms, int config_startup_time, unsigned char program_type,
		const char *progname, zbx_get_config_forks_f get_config_forks, const char *config_java_gateway,
		int config_java_gateway_port, const char *config_externalscripts,
		zbx_get_value_internal_ext_f get_value_internal_ext_cb, const char *config_ssh_key_location,
		const char *config_webdriver_url)
{
	struct zbx_json	json;
	int		ret;
	char		*error = NULL;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);

	zbx_json_init(&json, 1024);

	if (SUCCEED == (ret = trapper_item_test(jp, config_comms, config_startup_time, program_type, progname,
			get_config_forks, config_java_gateway, config_java_gateway_port, config_externalscripts,
			get_value_internal_ext_cb, config_ssh_key_location, config_webdriver_url, &json, &error)))
	{
		if (SUCCEED != zbx_tcp_send_bytes_to(sock, json.buffer, json.buffer_size, config_comms->config_timeout))
			zabbix_log(LOG_LEVEL_TRACE, "%s() failed sending item.test response", __func__);
	}
	else
	{
		if (SUCCEED != zbx_send_response(sock, ret, error, config_comms->config_timeout))
			zabbix_log(LOG_LEVEL_TRACE, "%s() failed sending item.test error response", __func__);

		zbx_free(error);
	}

	zbx_json_free(&json);
	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}