#include "lib_acl.h"
#include "http_plugin.h"

typedef struct {
	ACL_VSTRING *alias;
	ACL_VSTRING *cgi;
} CGI_MAP;

static ACL_HTABLE *__table = NULL;

static CGI_MAP *cgi_map_new(const char *alias, const char *cgi)
{
	CGI_MAP *map = (CGI_MAP*) acl_mycalloc(1, sizeof(CGI_MAP));

	map->alias = acl_vstring_alloc(256);
	map->cgi = acl_vstring_alloc(256);
	acl_vstring_strcpy(map->alias, alias);
	acl_vstring_strcpy(map->cgi, var_cfg_cgi_bin);
	acl_vstring_strcpy(map->cgi, cgi);
	return (map);
}

static void cgi_map_free(CGI_MAP *map)
{
	acl_vstring_free(map->alias);
	acl_vstring_free(map->cgi);
	acl_myfree(map);
}

void http_cgi_init()
{
	__table = acl_htable_create(10, 0);
}

void http_cgi_end()
{
	acl_htable_free(__table, (void (*)(void*)) cgi_map_free);
}

void http_cgi_add2(const char *alias, const char *cgi)
{
	CGI_MAP *map;

	map = (CGI_MAP*) acl_htable_find(__table, alias);
	if (map != NULL) {
		acl_msg_warn("%s(%d): alias(%s) already exist",
			__FUNCTION__, __LINE__, alias);
		return;
	}
	map = cgi_map_new(alias, cgi);
	acl_htable_enter(__table, alias, map);
}

void http_cgi_add1(const ACL_ARGV *args)
{
	if (args->argc < 2) {
		acl_msg_error("%s(%d): argc(%d) < 2",
			__FUNCTION__, __LINE__, args->argc);
		return;
	}

	http_cgi_add2(args->argv[0], args->argv[1]);
}

CGI *http_cgi_path(const char *alias)
{
	CGI *cgi;
	CGI_MAP *map = (CGI_MAP*) acl_htable_find(__table, alias);

	if (map == NULL)
		return (NULL);

	cgi = (CGI*) acl_mycalloc(1, sizeof(CGI));
	cgi->alias = acl_mystrdup(alias);
	cgi->cgi = acl_mystrdup(STR(map->cgi));
	return (cgi);
}

static void cgi_free(CGI *cgi)
{
	if (cgi == NULL)
		return;
	if (cgi->client)
		acl_vstream_close(cgi->client);
	if (cgi->hdr_req)
		http_hdr_req_free(cgi->hdr_req);
	if (cgi->req) {
		cgi->req->hdr_req = NULL;
		http_req_free(cgi->req);
	}
	if (cgi->hdr_res)
		http_hdr_res_free(cgi->hdr_res);
	if (cgi->cgi)
		acl_myfree(cgi->cgi);
	if (cgi->alias)
		acl_myfree(cgi->alias);
	acl_myfree(cgi);
}

static void run_cgi(CGI *cgi, ACL_ARGV *env)
{
	char *command = cgi->cgi;
	ACL_VSTREAM *stream;
	ACL_VSTRING *vbuf;
	ACL_ARGV *cgi_hdrs;
	char  buf[4096];
	int   ret, errnum = 200;
	char **cpp;

	/* 打开与CGI程序的管道数据流 */
	stream = acl_vstream_popen(O_RDWR,
			ACL_VSTREAM_POPEN_COMMAND, command,
			ACL_VSTREAM_POPEN_ENV, env->argv,
			ACL_VSTREAM_POPEN_END);

	if (stream == NULL) {
		acl_msg_error("%s(%d): open command(%s) error(%s)",
			__FUNCTION__, __LINE__, command, acl_last_serror());
		return;
	}

	cgi->req = http_req_new(cgi->hdr_req);
	if (cgi->hdr_req->hdr.content_length > 0) {
		while (1) {
			ret = (int) http_req_body_get_sync(cgi->req, cgi->client,
					buf, sizeof(buf) - 1);
			if (ret <= 0)
				break;
			if (acl_vstream_writen(stream, buf, ret) == ACL_VSTREAM_EOF) {
				acl_vstream_close(stream);
				return;
			}
		}
	}

	/*
	 * http_hdr_set_keepalive(cgi->hdr_req, cgi->hdr_res);
	 */

	cgi_hdrs = acl_argv_alloc(10);

	/* 读CGI发送的响应头 */
	while (1) {
		char *pname, *ptr;

		ret = acl_vstream_gets_nonl(stream, buf, sizeof(buf));
		if (ret == ACL_VSTREAM_EOF) {
			acl_msg_error("%s(%d): cgi exit exception",
				__FUNCTION__, __LINE__);
			errnum = 500;
			break;
		}
		if (ret == 0) {
			break;
		}
		ptr = buf;
		pname = acl_mystrtok(&ptr, ":\t ");
		if (ptr == NULL) {
			acl_msg_warn("%s(%d): cgi reply hdr(%s) invalid",
				__FUNCTION__, __LINE__, buf);
			continue;
		}
		acl_argv_add(cgi_hdrs, pname, ptr, NULL);
	}

	vbuf = acl_vstring_alloc(256);

	cgi->hdr_res = http_hdr_res_static(errnum);

	for (cpp = cgi_hdrs->argv; *cpp; cpp += 2) {
		http_hdr_put_str(&cgi->hdr_res->hdr, cpp[0], cpp[1]);
	}

	http_hdr_entry_replace(&cgi->hdr_res->hdr, "Connection", "close", 1);
	http_hdr_build(&cgi->hdr_res->hdr, vbuf);

	ret = acl_vstream_writen(cgi->client, STR(vbuf), LEN(vbuf));
	if (ret > 0) {
		/* 读CGI的响应数据体并发送至浏览器 */
		while (1) {
			ret = acl_vstream_read(stream, buf, sizeof(buf));
			if (ret == ACL_VSTREAM_EOF)
				break;
			if (acl_vstream_writen(cgi->client, buf, ret) == ACL_VSTREAM_EOF)
				break;
		}
	}

	acl_vstring_free(vbuf);
	acl_vstream_pclose(stream);
}

void http_cgi_thread(void *ctx)
{
	CGI *cgi = (CGI*) ctx;
	HTTP_HDR_REQ *hdr_req = cgi->hdr_req;
	ACL_ARGV *env = acl_argv_alloc(10);
	ACL_ITER iter;

	const char *reply = "HTTP/1.1 200 OK\r\n"
		"Date: Sun, 15 Nov 2009 07:06:22 GMT\r\n"
		"Server: Apache/2.2.9 (Unix)\r\n"
		"Connection: close\r\n"
		"Content-Type: text/html\r\n\r\n";
	acl_foreach(iter, hdr_req->hdr.entry_lnk) {
		HTTP_HDR_ENTRY *entry = (HTTP_HDR_ENTRY*) iter.data;

		if (entry->off)
			continue;
		acl_argv_add(env, entry->name, entry->value, NULL);
	}

	if (1)
		run_cgi(cgi, env);
	else {
		acl_vstream_writen(cgi->client, reply, sizeof(reply));
	}
	acl_argv_free(env);
	cgi_free(cgi);
}
