/*
** 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 "zabbix_stats.h"
#include "zbxsysinfo.h"

#include "../sysinfo.h"
#include "zbxnum.h"
#include "zbxcomms.h"
#include "zbxjson.h"

/******************************************************************************
 *                                                                            *
 * Purpose: checks whether JSON response is "success" or "failed"             *
 *                                                                            *
 * Parameters: response - [IN]                                                *
 *             result   - [OUT]                                               *
 *                                                                            *
 * Return value:  SUCCEED - processed successfully                            *
 *                FAIL - error occurred                                       *
 *                                                                            *
 ******************************************************************************/
static int	check_response(const char *response, AGENT_RESULT *result)
{
	struct zbx_json_parse	jp;
	char			buffer[MAX_STRING_LEN];

	if (SUCCEED != zbx_json_open(response, &jp))
	{
		SET_MSG_RESULT(result, zbx_strdup(NULL, "Value should be a JSON object."));
		return FAIL;
	}

	if (SUCCEED != zbx_json_value_by_name(&jp, ZBX_PROTO_TAG_RESPONSE, buffer, sizeof(buffer), NULL))
	{
		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot find tag: %s.", ZBX_PROTO_TAG_RESPONSE));
		return FAIL;
	}

	if (0 != strcmp(buffer, ZBX_PROTO_VALUE_SUCCESS))
	{
		if (SUCCEED != zbx_json_value_by_name(&jp, ZBX_PROTO_TAG_INFO, buffer, sizeof(buffer), NULL))
			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot find tag: %s.", ZBX_PROTO_TAG_INFO));
		else
			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain internal statistics: %s", buffer));

		return FAIL;
	}

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: sends Zabbix stats request and receives result data               *
 *                                                                            *
 * Parameters: json    - [IN] the request                                     *
 *             ip      - [IN] external Zabbix instance hostname               *
 *             port    - [IN] external Zabbix instance port                   *
 *             timeout - [IN] timeout value for comms                         *
 *             result  - [OUT] check result                                   *
 *                                                                            *
 ******************************************************************************/
static void	get_remote_zabbix_stats(const struct zbx_json *json, const char *ip, unsigned short port, int timeout,
		AGENT_RESULT *result)
{
	zbx_socket_t	s;

	if (SUCCEED == zbx_tcp_connect(&s, sysinfo_get_config_source_ip(), ip, port, timeout,
			ZBX_TCP_SEC_UNENCRYPTED, NULL, NULL))
	{
		if (SUCCEED == zbx_tcp_send(&s, json->buffer))
		{
			if (SUCCEED == zbx_tcp_recv(&s) && NULL != s.buffer)
			{
				if ('\0' == *s.buffer)
				{
					SET_MSG_RESULT(result, zbx_strdup(NULL,
							"Cannot obtain internal statistics: received empty response."));
				}
				else if (SUCCEED == check_response(s.buffer, result))
					zbx_set_agent_result_type(result, ITEM_VALUE_TYPE_TEXT, s.buffer);
			}
			else
			{
				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain internal statistics: %s",
						zbx_socket_strerror()));
			}
		}
		else
		{
			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain internal statistics: %s",
					zbx_socket_strerror()));
		}

		zbx_tcp_close(&s);
	}
	else
	{
		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain internal statistics: %s",
				zbx_socket_strerror()));
	}
}

/******************************************************************************
 *                                                                            *
 * Purpose: creates Zabbix stats request                                      *
 *                                                                            *
 * Parameters: ip      - [IN] external Zabbix instance hostname               *
 *             port    - [IN] external Zabbix instance port                   *
 *             timeout - [IN] timeout value for comms                         *
 *             result  - [OUT] check result                                   *
 *                                                                            *
 * Return value:  SUCCEED - processed successfully                            *
 *                FAIL - error occurred                                       *
 *                                                                            *
 ******************************************************************************/
int	zbx_get_remote_zabbix_stats(const char *ip, unsigned short port, int timeout, AGENT_RESULT *result)
{
	struct zbx_json	json;

	zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN);
	zbx_json_addstring(&json, ZBX_PROTO_TAG_REQUEST, ZBX_PROTO_VALUE_ZABBIX_STATS, ZBX_JSON_TYPE_STRING);

	get_remote_zabbix_stats(&json, ip, port, timeout, result);

	zbx_json_free(&json);

	return 0 == ZBX_ISSET_MSG(result) ? SUCCEED : FAIL;
}

/******************************************************************************
 *                                                                            *
 * Purpose: creates Zabbix stats queue request                                *
 *                                                                            *
 * Parameters: ip      - [IN] external Zabbix instance hostname               *
 *             port    - [IN] external Zabbix instance port                   *
 *             from    - [IN] lower limit for delay                           *
 *             to      - [IN] upper limit for delay                           *
 *             timeout - [IN] timeout value for comms                         *
 *             result  - [OUT] check result                                   *
 *                                                                            *
 * Return value:  SUCCEED - processed successfully                            *
 *                FAIL - error occurred                                       *
 *                                                                            *
 ******************************************************************************/
int	zbx_get_remote_zabbix_stats_queue(const char *ip, unsigned short port, const char *from, const char *to,
		int timeout, AGENT_RESULT *result)
{
	struct zbx_json	json;

	zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN);
	zbx_json_addstring(&json, ZBX_PROTO_TAG_REQUEST, ZBX_PROTO_VALUE_ZABBIX_STATS, ZBX_JSON_TYPE_STRING);
	zbx_json_addstring(&json, ZBX_PROTO_TAG_TYPE, ZBX_PROTO_VALUE_ZABBIX_STATS_QUEUE, ZBX_JSON_TYPE_STRING);

	zbx_json_addobject(&json, ZBX_PROTO_TAG_PARAMS);

	if (NULL != from && '\0' != *from)
		zbx_json_addstring(&json, ZBX_PROTO_TAG_FROM, from, ZBX_JSON_TYPE_STRING);
	if (NULL != to && '\0' != *to)
		zbx_json_addstring(&json, ZBX_PROTO_TAG_TO, to, ZBX_JSON_TYPE_STRING);

	zbx_json_close(&json);

	get_remote_zabbix_stats(&json, ip, port, timeout, result);

	zbx_json_free(&json);

	return 0 == ZBX_ISSET_MSG(result) ? SUCCEED : FAIL;
}

int	zabbix_stats(AGENT_REQUEST *request, AGENT_RESULT *result)
{
	const char	*ip_str, *port_str, *queue_str;
	unsigned short	port_number;

	if (5 < request->nparam)
	{
		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
		return SYSINFO_RET_FAIL;
	}

	if (NULL == (ip_str = get_rparam(request, 0)) || '\0' == *ip_str)
		ip_str = "127.0.0.1";

	if (NULL == (port_str = get_rparam(request, 1)) || '\0' == *port_str)
	{
		port_number = ZBX_DEFAULT_SERVER_PORT;
	}
	else if (SUCCEED != zbx_is_ushort(port_str, &port_number))
	{
		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
		return SYSINFO_RET_FAIL;
	}

	queue_str = get_rparam(request, 2);

	if (3 > request->nparam)
	{
		if (SUCCEED != zbx_get_remote_zabbix_stats(ip_str, port_number, request->timeout, result))
			return SYSINFO_RET_FAIL;
	}
	else if (NULL != queue_str && 0 == strcmp(queue_str, ZBX_PROTO_VALUE_ZABBIX_STATS_QUEUE))
	{
		if (SUCCEED != zbx_get_remote_zabbix_stats_queue(ip_str, port_number, get_rparam(request, 3),
				get_rparam(request, 4), request->timeout, result))
		{
			return SYSINFO_RET_FAIL;
		}
	}
	else
	{
		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
		return SYSINFO_RET_FAIL;
	}

	return SYSINFO_RET_OK;
}