/*
 * Copyright 1999 Caucho Technology all rights reserved.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <windows.h>
#include "httpext.h"
#include <errno.h>
#include <ctype.h>
#include <string.h>

extern "C" {
#include "../common/cse.h"
}

extern "C" {

char *
cse_alloc(config_t *config, int length)
{
  return (char *) malloc(length + 4);
}

char *
cse_strdup(config_t *config, const char *string)
{
  return strdup(string);
}

void
cse_error(config_t *config, char *fmt, ...)
{
#ifdef DEBUG
   va_list arg;
   FILE *file;
   file = fopen("/temp/iis.log", "a+b");
   va_start(arg, fmt);
   if (file && fmt)
		vfprintf(file, fmt, arg);
   va_end(arg);
   fclose(file);
#endif
}

void cse_free(config_t *config, void *value) {}

void
cse_set_socket_cleanup(int socket, void *pool)
{
}

void
cse_kill_socket_cleanup(int socket, void *pool)
{
}

}

void
cse_log(char *fmt, ...)
{
#ifdef DEBUG
	va_list arg;
	FILE *file;
	file = fopen("/temp/iis.log", "a+");
	va_start(arg, fmt);
	if (file)
		vfprintf(file, fmt, arg);
	va_end(arg);
	fclose(file);
#endif
}

void *
cse_create_lock()
{
	return CreateMutex(0, false, "resin");
}

int
cse_lock(void *lock)
{
	if (lock) {
		WaitForSingleObject(lock, INFINITE);
	}

	return 1;
}

void
cse_unlock(void *lock)
{
	if (lock) {
		ReleaseMutex(lock);
	}
}

static void
cse_printf(EXTENSION_CONTROL_BLOCK *r, char *fmt, ...)
{
  va_list args;
  char buf[4096];
  unsigned long len;

  va_start(args, fmt);
  vsprintf(buf, fmt, args);

  va_end(args);

  len = strlen(buf);

  r->WriteClient(r->ConnID, buf, &len, 0);
}


static int
connection_error(config_t *config, EXTENSION_CONTROL_BLOCK *r)
{
  // r->content_type = "text/html";
  // ap_send_http_header(r);
  char *hostname = 0;
  int port = 0;

  if (config->error_page) {
	  DWORD size = strlen(config->error_page);
	  DWORD type = 0;

	  r->ServerSupportFunction(r->ConnID, HSE_REQ_SEND_URL, 
		  config->error_page, &size, &type);
	  return 1;
  }

  cse_printf(r, "HTTP/1.0 500 Server Error\n\n");
  cse_printf(r, "<html><body bgcolor='white'>\n");
  cse_printf(r, "<h1>Cannot contact Servlet Runner at %s:%d\n",
	     (hostname ? hostname : "localhost"),
	     (port ? port : 6802));
  cse_printf(r, "</h1>\n");
  cse_printf(r, "</body></html>\n");

  return 1;
}

static void
write_var(stream_t *s, EXTENSION_CONTROL_BLOCK *r, char *name, int code)
{
	char buf[8192];
	unsigned long size = sizeof(buf);

	buf[0] = 0;
	if (r->GetServerVariable(r->ConnID, name, buf, &size) && size > 0 && buf[0]) {
		buf[size] = 0;
		cse_write_string(s, code, buf);
	}
}

static int 
write_env(stream_t *s, EXTENSION_CONTROL_BLOCK *r)
{
	int isHttp11 = 0;

	char protocol[8192];
	char buf[1024];
	unsigned long size = sizeof(protocol);

	if (r->GetServerVariable(r->ConnID, "SERVER_PROTOCOL", protocol, &size) && size > 0) {
		protocol[size] = 0;
		isHttp11 = ! strcmp(protocol, "HTTP/1.1");
	}

	write_var(s, r, "SERVER_PROTOCOL", CSE_PROTOCOL);
	write_var(s, r, "REQUEST_METHOD", CSE_METHOD);
	write_var(s, r, "PATH_INFO", CSE_URI);
//	write_var(s, r, "PATH_TRANSLATED", CSE_PATH_TRANSLATED);
	write_var(s, r, "QUERY_STRING", CSE_QUERY_STRING);
	write_var(s, r, "SERVER_NAME", CSE_SERVER_NAME);
	write_var(s, r, "SERVER_PORT", CSE_SERVER_PORT);
	write_var(s, r, "REMOTE_HOST", CSE_REMOTE_HOST);
	write_var(s, r, "REMOTE_ADDR", CSE_REMOTE_ADDR);
	write_var(s, r, "REMOTE_USER", CSE_REMOTE_USER);
	write_var(s, r, "AUTH_TYPE", CSE_AUTH_TYPE);

	write_var(s, r, "CONTENT_TYPE", CSE_CONTENT_TYPE);
	write_var(s, r, "CONTENT_LENGTH", CSE_CONTENT_LENGTH);
  // cse_write_string(s, CSE_DOCROOT, ap_document_root(r));
	sprintf(buf, "%d", s->srun->session);
	cse_write_string(s, CSE_SESSION_GROUP, buf);

	if (r->GetServerVariable(r->ConnID, "SERVER_PORT_SECURE", buf, &size) && size > 0) {
		if (buf[0] == '1') {
			cse_write_string(s, CSE_IS_SECURE, "");
		}
	}


	return isHttp11;
}

static void
write_headers(stream_t *s, EXTENSION_CONTROL_BLOCK *r)
{
	char buf[16384];
	unsigned long i = 0;
	unsigned long len = sizeof(buf);

	if (! r->GetServerVariable(r->ConnID, "ALL_RAW", buf, &len))
		return;

	while (i < len) {
		for (; i < len && isspace(buf[i]); i++) {
		}

		int head = i;
		int tail;

		for (; i < len && buf[i] != ':'; i++) {
		}
		buf[i] = 0;

		tail = ++i;
		for (; i < len && (buf[i] != '\n' || isspace(buf[i+1])); i++) {
			if (isspace(buf[i]))
				buf[i] = ' ';
		}
		if (buf[i - 1] == ' ')
			buf[i - 1] = 0;
		buf[i++] = 0;
		if (buf[head]) {
			cse_write_string(s, CSE_HEADER, buf + head);
			cse_write_string(s, CSE_VALUE, buf + tail);
		}
	}
}

static int
cse_write_response(stream_t *s, unsigned long len, EXTENSION_CONTROL_BLOCK *r)
{
	while (len > 0) {
		unsigned long sublen;

		if (s->read_offset >= s->read_length) {
			if (cse_fill_buffer(s) < 0)
				return -1;
		}

		sublen = s->read_length - s->read_offset;
		if (len < sublen)
			sublen = len;

		s->read_buf[s->read_length] = 0;
		unsigned long writelen = sublen;
		while (writelen > 0) {
			unsigned long sentlen = writelen;

			if (! r->WriteClient(r->ConnID, s->read_buf + s->read_offset, &sentlen, HSE_IO_SYNC) || 
				sentlen <= 0) {
				cse_close(s, "response");
				return -1;
			}

			writelen -= sentlen;
		}
		s->read_offset += sublen;
		len -= sublen;
	}

	return 1;
}

static int
cse_copy_results(stream_t *s, EXTENSION_CONTROL_BLOCK *r, config_t *config)
{
	char headers[8192];
	char status[1024];
	char buf[8192];
	char key[1024];
	char value[1024];
	int code;

	headers[0] = 0;
	do {
		int i;
		int l1, l2, l3;
		int read_len;
		unsigned long size;

		code = cse_read_byte(s);
		l1 = cse_read_byte(s) & 0xff;
		l2 = cse_read_byte(s) & 0xff;
		l3 = cse_read_byte(s);
		read_len = (l1 << 16) + (l2 << 8) + (l3 & 0xff);

		if (l3 < 0 || s->socket < 0)
			return -1;

		LOG(("code %c(%d) %d\n", code, code, read_len));

		switch (code) {
		case CSE_DATA:
			if (cse_write_response(s, read_len, r) < 0)
				code = -1;
			break;

		case CSE_STATUS:
			cse_read_all(s, status, read_len);
			status[read_len] = 0;

			for (i = 0; status[i] && status[i] != ' '; i++) {
			}
			i++;

			strcat(headers, status + i);
			break;

		case CSE_HEADER:
			cse_read_all(s, key, read_len);
			key[read_len] = 0;
			cse_read_string(s, value, sizeof(value));
			strcat(headers, "\r\n");
			strcat(headers, key);
			strcat(headers, ": ");
			strcat(headers, value);
			break;

		case CSE_SEND_HEADER:
			size = strlen(headers);
			r->ServerSupportFunction(r, HSE_REQ_SEND_RESPONSE_HEADER, headers, &size, 0);
			break;

		default:
			if (code < 0 || read_len < 0 || read_len > 8192) {
				code = -1;
				break;
			}

			cse_read_all(s, buf, read_len);
			break;
		}
	} while (code > 0 && code != CSE_END && code != CSE_ACK && code != CSE_CLOSE);

	return code;
}

static void
cse_get_ip(EXTENSION_CONTROL_BLOCK *r, char *ip, unsigned long length)
{
	ip[0] = 0;
	if (r->GetServerVariable(r->ConnID, "REMOTE_ADDR", ip, &length) && length >= 0)
		ip[length] = 0;
	else
		ip[0] = 0;
}

static int
get_session_index(EXTENSION_CONTROL_BLOCK *r)
{
	char buf[16384];
	unsigned long i = 0;
	unsigned long len = sizeof(buf);
	int session;

	if (! r->GetServerVariable(r->ConnID, "ALL_HTTP", buf, &len))
		return -1;

	while (i < len) {
		int head = i + 5;
		int tail;
		i = head;
		for (; i < len && buf[i] != ':'; i++) {
		}
		tail = i;

		for (i++; i < len && buf[i] != '\r' && buf[i] != '\n'; i++) {
		}

		if (head >= tail)
			continue;

		buf[tail] = 0;
		buf[i] = 0;

		if (stricmp(buf + head, "cookie"))
			continue;

	    session = cse_session_from_string(buf + tail + 1, "JSESSIONID=");
		if (session >= 0)
			return session;
	}

	len = sizeof(buf);

	buf[0] = 0;
	if (! r->GetServerVariable(r->ConnID, "PATH_INFO", buf, &len))
		return -1;
	
	buf[len] = 0;
	return cse_session_from_string(buf, ";jsessionid=");
}

static int
write_request(stream_t *s, EXTENSION_CONTROL_BLOCK *r, config_t *config)
{
	int code;
	char ip[1024];
	int session_index = get_session_index(r);
	int rand = 0;

	DWORD request_time = GetTickCount() / 1000;

	cse_get_ip(r, ip, sizeof(ip));
	if (! cse_open_connection(s, config, session_index, ip,
				  request_time, rand, 0))
		return connection_error(config, r);

	int isHttp11 = write_env(s, r);
	write_headers(s, r);

	// read post data
	char buf[BUF_LENGTH];
	int isFirst = 1;
	unsigned long totalLen = 0;
		
	while (totalLen < r->cbTotalBytes) {
		unsigned long len = sizeof(buf);

		if (r->cbAvailable > 0) {
			if (len > r->cbAvailable)
				len = r->cbAvailable;
			memcpy(buf, r->lpbData + totalLen, len);
			r->cbAvailable -= len;
		}
		else if (! r->ReadClient(r->ConnID, buf, &len) || len <= 0) {
			break;
		}

		totalLen += len;

		if (! isFirst) {
			code = cse_copy_results(s, r, config);

			if (code < 0 || code == CSE_END || code == CSE_CLOSE)
				break;
		}

		cse_write_packet(s, CSE_DATA, buf, len);
		isFirst = 0;
	}	

	cse_write_packet(s, CSE_END, 0, 0);

	code = cse_copy_results(s, r, config);

	if (code == CSE_END)
		cse_recycle(s);
	else
		cse_close(s, "write");

	return code == CSE_END || code == CSE_CLOSE;
}

static int g_count;

static void
jvm_status(config_t *config, EXTENSION_CONTROL_BLOCK *r)
{
  int i;
  stream_t s;

  cse_printf(r, "<center><table border=2 cellspacing=0 cellpadding=2 width=\"80%%\">\n");
  cse_printf(r, "<tr><th>Host\n");
  cse_printf(r, "    <th>Minute<br>%%cpu/thread\n");
  cse_printf(r, "    <th>Hour<br>%%cpu/thread\n");
  cse_printf(r, "    <th>Day<br>%%cpu/thread\n");

  for (i = 0; i < config->srun_size; i++) {
    srun_t *srun = config->srun_list + i;
    int port = srun->port;

    cse_printf(r, "<tr>");

    if (! cse_open(&s, config, srun, 0)) {
      cse_printf(r, "<td bgcolor='#ff6666'>%s:%d%s</td><td colspan=3>&nbsp;",
		 srun->hostname ? srun->hostname : "localhost",
		 port, srun->is_backup ? "*" : "");
    }
    else {
      cse_printf(r, "<td bgcolor='#66ff66'>%s:%d%s</td>",
		 srun->hostname ? srun->hostname : "localhost",
		 port, srun->is_backup ? "*" : "");

      cse_recycle(&s);
      continue;
    }
  }

  cse_printf(r, "</table></center>\n");
}

static void
cse_caucho_status(config_t *config, EXTENSION_CONTROL_BLOCK *r)
{
  location_t *loc;
  char *headers = "200 OK\r\nContent-Type: text/html";
  unsigned long size = strlen(headers);

  r->ServerSupportFunction(r, HSE_REQ_SEND_RESPONSE_HEADER, headers, &size, 0);
 
  cse_printf(r, "<html><title>Status : Caucho Servlet Engine</title>\n");
  cse_printf(r, "<body bgcolor=white>\n");
  cse_printf(r, "<h1>Status : Caucho Servlet Engine</h1>\n");
  
  jvm_status(config, r);

  cse_printf(r, "<p><center><table border=2 cellspacing=0 cellpadding=2 width=\"80%%\">\n");
  cse_printf(r, "<tr><th width='50%'>Host\n");
  cse_printf(r, "    <th>url-pattern\n");
  
  loc = config ? config->locations : 0;
  for (; loc; loc = loc->next) {
    cse_printf(r, "<tr bgcolor='#ffcc66'><td>%s<td>%s%s%s%s</tr>\n", 
	       loc->host && loc->host[0] ? loc->host : "*",
	       loc->prefix,
	       ! loc->is_exact && ! loc->suffix ? "/*" : 
	       loc->suffix && loc->prefix[0] ? "/" : "",
	       loc->suffix ? "*" : "",
	       loc->suffix ? loc->suffix : "");
  }

  cse_printf(r, "</table></center>\n");
  cse_printf(r, "</body></html>\n");
}

int
cse_handle_request(config_t *config, EXTENSION_CONTROL_BLOCK *r)
{
	char url[8192];
	unsigned long size = sizeof(url);

	url[0] = 0;
	if (r->GetServerVariable(r->ConnID, "PATH_INFO", url, &size) && size > 0 && url[0]) {
		url[size] = 0;
		if (! strcmp(url, "/caucho-status")) {
			cse_caucho_status(config, r);
			return 1;
		}
	}

	stream_t s;

	return write_request(&s, r, config);
}
