#include "lib_acl.h"
#include "lib_protocol.h"

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

#include "probe.h"

ACL_ARRAY *var_probe_server_link = NULL;
const char *var_probe_logfile = NULL;
int   var_probe_loglevel = 0;
int   var_probe_statlen = 1024;
int   var_probe_fork_sleep = 1;
ACL_VSTREAM *var_probe_debug_fp = NULL;

static ACL_XINETD_CFG_PARSER *__cfg_parser = NULL;

static char request_format[] = "\
GET %s HTTP/1.1\r\n\
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n\
Accept-Language: zh-cn\r\n\
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)\r\n\
Accept-Encoding: gzip, deflate\r\n\
Host: %s:%d\r\n\r\n";

static int  __parse_url(const char *url, char *host_buf, int hlen,
	int *http_port, char *url_buf, int ulen)
{
	/* url format: http://www.hexun.com[:80]/index.html */
	char *ptr, *url_begin;
	char  buf[1024];
	size_t n;

	snprintf(buf, sizeof(buf) - 1, "%s", url);
	ptr = buf;
	while (*ptr == ' ' || *ptr == '\t')
		ptr++;

	n = strlen("http://");

	/* 是否标准的 http 协议请求格式 */
	if (strncasecmp(ptr, "http://", n) == 0)
		ptr += n;

	if (*ptr == 0)
		return -1;

	url_begin = ptr;
	ptr = strchr(url_begin, '/');
	if (ptr) {
		*ptr++ = 0;
		if (*ptr == 0)
			strcpy(url_buf, "/");
		else
			snprintf(url_buf, ulen, "/%s", ptr);
	} else
		strcpy(url_buf, "/");

	/* 获得 http 协议端口号 */
	if (http_port) {
		ptr = strchr(url_begin, ':');
		if (ptr) {
			*ptr++ = 0;
			n = atoi(ptr);
			if (n <= 0)
				*http_port = 80;
			else
				*http_port = (int) n;
		} else
			*http_port = 80;
	}

	snprintf(host_buf, hlen, "%s", url_begin);
	return 0;
}

static void __build_http_request(PROBE_SERVER *server)
{
	const char *myname = "__build_http_request";
	char  host_refer[256], url_request[256];
	int   port_refer = 80;
	int   ret;

	memset(host_refer, 0, sizeof(host_refer));
	memset(url_request, 0, sizeof(url_request));

	ret = __parse_url(server->url, host_refer, sizeof(host_refer),
			&port_refer, url_request, sizeof(url_request));
	if (ret < 0)
		acl_msg_fatal("%s(%d), %s: invalid url=[%s]",
			__FILE__, __LINE__, myname, server->url);

	snprintf(server->http_request_header,
		sizeof(server->http_request_header) - 1, request_format,
			url_request, host_refer, port_refer);

	server->http_request_len = (int) strlen(server->http_request_header);
}

static void __set_name_value(char *data, PROBE_SERVER *server)
{
	/* data: name=value */
	const char *myname = "__set_name_value";
	char *pname, *pvalue, *ptr;

	pname = data;
	/* skip ' ' and '\t' */
	while (*pname == ' ' || *pname == '\t')
		pname++;
	if (pname == 0) {
		acl_msg_error("%s(%d), %s: invalid data=[%s]",
			__FILE__, __LINE__, myname, data);
		return;
	}

	pvalue = pname;
	/* find '=' */
	while (*pvalue) {
		if (*pvalue == '=') {
			*pvalue++ = 0;
			break;
		}
		pvalue++;
	}

	/* skip ' ' and '\t' */
	while (*pvalue == ' ' || *pvalue == '\t')
		pvalue++;
	if (pvalue == 0) {
		acl_msg_error("%s(%d)->%s: invalid data=[%s]",
			__FILE__, __LINE__, myname, data);
		return;
	}

	ptr = pvalue;
	while (*ptr) {
		if (*ptr == ' ' || *ptr == '\t' || *ptr == ',') {
			*ptr = 0;
			break;
		}
		ptr++;
	}

	if (strcasecmp(pname, VAR_CFG_SERVER_NAME) == 0)
		server->name = acl_mystrdup(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_ADDR) == 0)
		server->addr = acl_mystrdup(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_URL) == 0)
		server->url = acl_mystrdup(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_CONNECT_TIMEOUT) == 0)
		server->connect_timeout = atoi(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_RW_TIMEOUT) == 0)
		server->rw_timeout = atoi(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_RETRY_INTER) == 0)
		server->retry_inter = atoi(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_PROBE_INTER) == 0)
		server->probe_inter = atoi(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_HTTP_ERRORS) == 0)
		server->http_status_errors = acl_mystrdup(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_LOG) == 0)
		server->logfile = acl_mystrdup(pvalue);
	else if (strcasecmp(pname, VAR_CFG_SERVER_WARN_TIME) == 0)
		server->warn_time = atoi(pvalue);

	/* only for test */
	printf("name(%s)=value(%s), probe_inter=%d\n",
		pname, pvalue, server->probe_inter);
}

static PROBE_SERVER *__cfg_server_new(char *data)
{
/*
 * data: server_name="博客2", addr=10.0.90.246:80, url=http://blog.hexun.com/index.aspx, connect_timeout=60, rw_timeout=60, retry_inter=5, probe_inter=5
 */
	const char *myname = "__cfg_server_new";
	PROBE_SERVER *server = NULL;
	char *ptr;

	if (data == NULL || *data == 0) {
		acl_msg_error("%s: invalid data input", myname);
		return NULL;
	}

	server = (PROBE_SERVER *) acl_mycalloc(1, sizeof(PROBE_SERVER));
	if (server == NULL)
		acl_msg_fatal("%s: can't calloc, serr=%s",
			myname, strerror(errno));

	while (1) {
		ptr = acl_mystrtok(&data, ",");
		if (ptr == NULL)
			break;

		__set_name_value(ptr, server);
	}

	__build_http_request(server);
	if (server->logfile != NULL) {
		server->logfp = acl_vstream_fopen(server->logfile,
			O_WRONLY | O_CREAT | O_APPEND, 0600, 4096);
		if (server->logfp == NULL)
			acl_msg_fatal("%s: open logfile(%s) error %s ",
				myname, server->logfile, strerror(errno));
	} else
		server->logfp = NULL;

	return server;
}

static void __cfg_parse_one_server(const char *server_data)
{
	const char *myname = "__cfg_parse_one_server";
	char *buf = NULL;
	char *begin, *end;
	PROBE_SERVER *server;

#undef	RETURN
#define	RETURN do { \
	if (buf) \
		acl_myfree(buf); \
	return; \
} while(0)

	if (server_data == NULL || *server_data == 0)
		acl_msg_fatal("%s(%d)->%s: input invalid",
			__FILE__, __LINE__, myname);

	buf = acl_mystrdup(server_data);

	begin = buf;
	while (*begin) {
		if (*begin == '<') {
			*begin++ = 0;
			break;
		}
		begin++;
	}

	if (*begin == 0)
		RETURN;
	end = begin;
	while (*end) {
		if (*end == '>') {
			*end = 0;
			break;
		}
		end++;
	}

	server = __cfg_server_new(begin);
	if (server == NULL)
		acl_msg_fatal("%s(%d)->%s: invalid data=[%s]",
			__FILE__, __LINE__, myname, server_data);
	else
		acl_array_append(var_probe_server_link, (void *) server);

	acl_msg_info("%s(%d)->%s: name=%s, addr=%s, url=%s, connect_timeout=%d,"
		" rw_timeout=%d, retry_inter=%d, probe_inter=%d\n",
		__FILE__, __LINE__, myname, server->name, server->addr,
		server->url, server->connect_timeout, server->rw_timeout,
		server->retry_inter, server->probe_inter);

	RETURN;
}

static void __cfg_server_parse(const ACL_ARRAY *a)
{
	int   i, n;

	if (a == NULL)
		return;

	n = acl_array_size(a);

#if 0
	for (i = 0; i < n; i++)
		acl_msg_info("%s: %s", VAR_CFG_SERVER, (const char *) acl_array_index(a, i));
#endif

	for (i = 0; i < n; i++)
		__cfg_parse_one_server((const char *) acl_array_index(a, i));
}

void probe_cfg_load(void)
{
	const char *myname = "probe_cfg_load";
	ACL_XINETD_CFG_PARSER *xcp;
	const ACL_ARRAY *servers_array;
	const char *ptr;

	if (var_cfg_file == NULL || *var_cfg_file == 0)
		acl_msg_fatal("%s(%d)->%s: __cfg_file null",
			__FILE__, __LINE__, myname);

	xcp = acl_xinetd_cfg_load(var_cfg_file);
	if (xcp == NULL) {
		acl_msg_error("%s(%d)->%s: xinetd_cfg_load return null, file=%s",
			__FILE__, __LINE__, myname, var_cfg_file);
		exit (1);
	}
	__cfg_parser = xcp;

	var_probe_server_link = acl_array_create(50);
	if (var_probe_server_link == NULL)
		acl_msg_fatal("%s(%d)->%s: acl_array_create error, serr=%s",
			__FILE__, __LINE__, myname, strerror(errno));

	servers_array = acl_xinetd_cfg_get_ex(xcp, VAR_CFG_SERVER);
	if (servers_array == NULL)
		acl_msg_fatal("%s(%d)->%s: not fine %s line",
			__FILE__, __LINE__, myname, VAR_CFG_SERVER);

	__cfg_server_parse(servers_array);

	var_probe_logfile = acl_xinetd_cfg_get(xcp, VAR_CFG_LOGFILE);
	if (var_probe_logfile == NULL || *var_probe_logfile == 0)
		acl_msg_warn("%s(%d)->%s: null var_probe_logfile",
			__FILE__, __LINE__, myname);
	ptr = acl_xinetd_cfg_get(xcp, VAR_CFG_LOGLEVEL);
	if (ptr && *ptr)
		var_probe_loglevel = atoi(ptr);
	if (var_probe_loglevel < 0)
		var_probe_loglevel = 0;
	ptr = acl_xinetd_cfg_get(xcp, VAR_CFG_STATLEN);
	if (ptr && *ptr)
		var_probe_statlen = atoi(ptr);
	if (var_probe_statlen <= 0)
		var_probe_statlen = 1024;

	ptr = acl_xinetd_cfg_get(xcp, VAR_CFG_FORK_SLEEP);
	if (ptr && *ptr)
		var_probe_fork_sleep = atoi(ptr);
	if (var_probe_fork_sleep < 0)
		var_probe_fork_sleep = 1;

	ptr = acl_xinetd_cfg_get(xcp, VAR_CFG_DEBUG_FILE);
	if (ptr && *ptr)
		var_probe_debug_fp = acl_vstream_fopen(ptr,
			O_WRONLY | O_CREAT | O_APPEND, 0600, 4096);
	else
		var_probe_debug_fp = NULL;
}
