/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**/

#include "checks_java.h"

#include "log.h"
#include "zbxjson.h"
#include "zbxsysinfo.h"

static int	parse_response(AGENT_RESULT *results, int *errcodes, int num, char *response,
		char *error, int max_error_len)
{
	const char		*p;
	struct zbx_json_parse	jp, jp_data, jp_row;
	char			*value = NULL;
	size_t			value_alloc = 0;
	int			i, ret = GATEWAY_ERROR;

	if (SUCCEED == zbx_json_open(response, &jp))
	{
		if (SUCCEED != zbx_json_value_by_name_dyn(&jp, ZBX_PROTO_TAG_RESPONSE, &value, &value_alloc, NULL))
		{
			zbx_snprintf(error, max_error_len, "No '%s' tag in received JSON", ZBX_PROTO_TAG_RESPONSE);
			goto exit;
		}

		if (0 == strcmp(value, ZBX_PROTO_VALUE_SUCCESS))
		{
			if (SUCCEED != zbx_json_brackets_by_name(&jp, ZBX_PROTO_TAG_DATA, &jp_data))
			{
				zbx_strlcpy(error, "Cannot open data array in received JSON", max_error_len);
				goto exit;
			}

			p = NULL;

			for (i = 0; i < num; i++)
			{
				if (SUCCEED != errcodes[i])
					continue;

				if (NULL == (p = zbx_json_next(&jp_data, p)))
				{
					zbx_strlcpy(error, "Not all values included in received JSON", max_error_len);
					goto exit;
				}

				if (SUCCEED != zbx_json_brackets_open(p, &jp_row))
				{
					zbx_strlcpy(error, "Cannot open value object in received JSON", max_error_len);
					goto exit;
				}

				if (SUCCEED == zbx_json_value_by_name_dyn(&jp_row, ZBX_PROTO_TAG_VALUE, &value,
						&value_alloc, NULL))
				{
					zbx_set_agent_result_type(&results[i], ITEM_VALUE_TYPE_TEXT, value);
					errcodes[i] = SUCCEED;
				}
				else if (SUCCEED == zbx_json_value_by_name_dyn(&jp_row, ZBX_PROTO_TAG_ERROR, &value,
						&value_alloc, NULL))
				{
					SET_MSG_RESULT(&results[i], zbx_strdup(NULL, value));
					errcodes[i] = NOTSUPPORTED;
				}
				else
				{
					SET_MSG_RESULT(&results[i], zbx_strdup(NULL, "Cannot get item value or error message"));
					errcodes[i] = AGENT_ERROR;
				}
			}

			ret = SUCCEED;
		}
		else if (0 == strcmp(value, ZBX_PROTO_VALUE_FAILED))
		{
			if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PROTO_TAG_ERROR, error, max_error_len, NULL))
				ret = NETWORK_ERROR;
			else
				zbx_strlcpy(error, "Cannot get error message describing reasons for failure", max_error_len);

			goto exit;
		}
		else
		{
			zbx_snprintf(error, max_error_len, "Bad '%s' tag value '%s' in received JSON",
					ZBX_PROTO_TAG_RESPONSE, value);
			goto exit;
		}
	}
	else
	{
		zbx_strlcpy(error, "Cannot open received JSON", max_error_len);
		goto exit;
	}
exit:
	zbx_free(value);

	return ret;
}

int	get_value_java(unsigned char request, const DC_ITEM *item, AGENT_RESULT *result, int config_timeout)
{
	int	errcode = SUCCEED;

	get_values_java(request, item, result, &errcode, 1, config_timeout);

	return errcode;
}

void	get_values_java(unsigned char request, const DC_ITEM *items, AGENT_RESULT *results, int *errcodes, int num,
		int config_timeout)
{
	zbx_socket_t	s;
	struct zbx_json	json;
	char		error[MAX_STRING_LEN];
	int		i, j, err = SUCCEED;

	zabbix_log(LOG_LEVEL_DEBUG, "In %s() jmx_endpoint:'%s' num:%d", __func__, items[0].jmx_endpoint, num);

	for (j = 0; j < num; j++)	/* locate first supported item to use as a reference */
	{
		if (SUCCEED == errcodes[j])
			break;
	}

	if (j == num)	/* all items already NOTSUPPORTED (with invalid key or port) */
		goto out;

	zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN);

	if (NULL == CONFIG_JAVA_GATEWAY || '\0' == *CONFIG_JAVA_GATEWAY)
	{
		err = GATEWAY_ERROR;
		zbx_strscpy(error, "JavaGateway configuration parameter not set or empty");
		goto exit;
	}

	if (ZBX_JAVA_GATEWAY_REQUEST_INTERNAL == request)
	{
		zbx_json_addstring(&json, ZBX_PROTO_TAG_REQUEST, ZBX_PROTO_VALUE_JAVA_GATEWAY_INTERNAL,
				ZBX_JSON_TYPE_STRING);
	}
	else if (ZBX_JAVA_GATEWAY_REQUEST_JMX == request)
	{
		for (i = j + 1; i < num; i++)
		{
			if (SUCCEED != errcodes[i])
				continue;

			if (0 != strcmp(items[j].username, items[i].username) ||
					0 != strcmp(items[j].password, items[i].password) ||
					0 != strcmp(items[j].jmx_endpoint, items[i].jmx_endpoint))
			{
				err = GATEWAY_ERROR;
				zbx_strscpy(error, "Java poller received items with different connection parameters");
				goto exit;
			}
		}

		zbx_json_addstring(&json, ZBX_PROTO_TAG_REQUEST, ZBX_PROTO_VALUE_JAVA_GATEWAY_JMX, ZBX_JSON_TYPE_STRING);

		if ('\0' != *items[j].username)
		{
			zbx_json_addstring(&json, ZBX_PROTO_TAG_USERNAME, items[j].username, ZBX_JSON_TYPE_STRING);
		}
		if ('\0' != *items[j].password)
		{
			zbx_json_addstring(&json, ZBX_PROTO_TAG_PASSWORD, items[j].password, ZBX_JSON_TYPE_STRING);
		}
		if ('\0' != *items[j].jmx_endpoint)
		{
			zbx_json_addstring(&json, ZBX_PROTO_TAG_JMX_ENDPOINT, items[j].jmx_endpoint,
					ZBX_JSON_TYPE_STRING);
		}
	}
	else
		assert(0);

	zbx_json_addarray(&json, ZBX_PROTO_TAG_KEYS);
	for (i = j; i < num; i++)
	{
		if (SUCCEED != errcodes[i])
			continue;

		zbx_json_addstring(&json, NULL, items[i].key, ZBX_JSON_TYPE_STRING);
	}
	zbx_json_close(&json);

	if (SUCCEED == (err = zbx_tcp_connect(&s, CONFIG_SOURCE_IP, CONFIG_JAVA_GATEWAY, CONFIG_JAVA_GATEWAY_PORT,
			config_timeout, ZBX_TCP_SEC_UNENCRYPTED, NULL, NULL)))
	{
		zabbix_log(LOG_LEVEL_DEBUG, "JSON before sending [%s]", json.buffer);

		if (SUCCEED == (err = zbx_tcp_send(&s, json.buffer)))
		{
			if (SUCCEED == (err = zbx_tcp_recv(&s)))
			{
				zabbix_log(LOG_LEVEL_DEBUG, "JSON back [%s]", s.buffer);

				err = parse_response(results, errcodes, num, s.buffer, error, sizeof(error));
			}
		}

		zbx_tcp_close(&s);
	}

	zbx_json_free(&json);

	if (FAIL == err)
	{
		zbx_strscpy(error, zbx_socket_strerror());
		err = GATEWAY_ERROR;
	}
exit:
	if (NETWORK_ERROR == err || GATEWAY_ERROR == err)
	{
		zabbix_log(LOG_LEVEL_DEBUG, "getting Java values failed: %s", error);

		for (i = j; i < num; i++)
		{
			if (SUCCEED != errcodes[i])
				continue;

			SET_MSG_RESULT(&results[i], zbx_strdup(NULL, error));
			errcodes[i] = err;
		}
	}
out:
	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
}