/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/* Cherokee
 *
 * Authors:
 *      Alvaro Lopez Ortega <alvaro@alobbs.com>
 *      Stefan de Konink <stefan@konink.de>
 *
 * Copyright (C) 2001-2008 Alvaro Lopez Ortega
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include "handler_php.h"
#include <cherokee/cherokee.h>
#include <cherokee/bogotime.h>
#include <cherokee/post.h>
#include <cherokee/connection-protected.h>

#define PHP  1

#ifdef PHP
#include "php.h"
#include "php_main.h"
#include "php_ini.h"
#include "php_variables.h"
#include "zend_highlight.h"
#include "SAPI.h"

#endif 

/*
#include "util.h"
#include "connection.h"
#include "connection-protected.h"
#include "server.h"
#include "server-protected.h"
#include "plugin_loader.h"
#include "connection_info.h"
*/

#ifdef PHP

/* PHP part
 */

static zend_module_entry cherokee_module_entry = {
	STANDARD_MODULE_HEADER,
	"cherokee",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NO_VERSION_YET,
	STANDARD_MODULE_PROPERTIES
};

static int
php_cherokee_sapi_startup(sapi_module_struct *sapi_module)
{
	return php_module_startup(sapi_module, &cherokee_module_entry, 1);
}

static int
php_cherokee_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
{
    cherokee_handler_php_t *hdl = SG(server_context);
    ret_t ret = cherokee_buffer_add(&hdl->output_buffer, str, str_length);

    if (unlikely (ret != ret))
    	php_handle_aborted_connection();

    return str_length; /* we always consume all the data passed to us. */
}

static void
php_cherokee_sapi_flush(void *server_context)
{
    cherokee_handler_php_t *hdl;
    TSRMLS_FETCH();

    /* If we haven't registered a server_context yet,
     * then don't bother flushing. */
    if (!server_context) {
        return;
    }

    hdl = SG(server_context);
    sapi_send_headers(TSRMLS_C);
    SG(headers_sent) = 1;

    hdl->phase = script_flushing;
}

static char *
php_cherokee_sapi_getenv(char *name, size_t name_len TSRMLS_DC)
{
    cherokee_handler_php_t *hdl = SG(server_context);
    return HDL_PHP_PROPS(hdl)->system_env.buf;
}

static int
php_cherokee_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{
    cherokee_handler_php_t *hdl = SG(server_context);
    cherokee_connection_t *conn = HANDLER_CONN(hdl);
    sapi_header_struct *h;
    zend_llist_position pos;
    zend_bool ignore_status = 0;
    int response_status = SG(sapi_headers).http_response_code;

//    conn->error_code = SG(sapi_headers).http_response_code; TODO

    if (!SG(request_info).content_type) {
	SG(request_info).content_type = sapi_get_default_content_type(TSRMLS_C);
	cherokee_buffer_add_str(&hdl->header_buffer, "Content-Type: ");
	cherokee_buffer_add(&hdl->header_buffer, SG(request_info).content_type, strlen(SG(request_info).content_type));
        cherokee_buffer_add_str(&hdl->header_buffer, CRLF);
        /* content length meenemen? */
    }

    if (SG(request_info).no_headers == 1) {
        return  SAPI_HEADER_SENT_SUCCESSFULLY;
    }

    h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos); 
    while (h) {
        /* prevent CRLFCRLF */
        if (h->header_len) {
            if (h->header_len > sizeof("Status:")-1 &&
                strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0) {
                if (!ignore_status) {
                    ignore_status = 1;
                    cherokee_buffer_add(&hdl->header_buffer, h->header, h->header_len);
                    cherokee_buffer_add_str(&hdl->header_buffer, CRLF);
                }
            } else if (response_status == 304 && h->header_len > sizeof("Content-Type:")-1 &&
                    strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:")-1) == 0) {
                h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
                continue;
            } else {
                cherokee_buffer_add(&hdl->header_buffer, h->header, h->header_len);
                cherokee_buffer_add_str(&hdl->header_buffer, CRLF);
            }
        }
        h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
    }

    return SAPI_HEADER_SENT_SUCCESSFULLY;
}

static int
php_cherokee_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
{
    
    /* TODO
    cherokee_handler_php_t *hdl = SG(server_context);
    cherokee_connection_t *conn = HANDLER_CONN(hdl);

    if (conn->post.has_info) {
        off_t post_len = conn->post.len;
        if (conn->post.len > count_bytes)
		post_len = count_bytes;


        return 0;
    }*/

    return 0;
}

static char *
php_cherokee_sapi_read_cookies(TSRMLS_D)
{
    char *p;
    cuint_t p_len;
    cherokee_handler_php_t *hdl = SG(server_context);
    cherokee_connection_t *conn = HANDLER_CONN(hdl);
    ret_t ret = cherokee_header_get_known (&conn->header, header_cookie, &p, &p_len);

    return p;
}

/*
static ret_t
foreach_header_add_unknown_variable (cherokee_buffer_t *header,
                                     cherokee_buffer_t *content,
                                     void              *data)
{
        cuint_t                      i;
        cherokee_handler_php_t *hdl = HDL_PHP(data);
	int new_val_len;

        for (i=0; i<header->len; i++) {
                if ((header->buf[i] >= 'a') &&
                    (header->buf[i] <= 'z'))
                {
                        header->buf[i] -= ('a' - 'A');
                } else if (header->buf[i] == '-') {
                        header->buf[i] = '_';
                }
        }

        cherokee_buffer_prepend_str (header, "HTTP_");

	if (sapi_module.input_filter(PARSE_SERVER, header->buf, content->buf, content->len, &new_val_len TSRMLS_CC)) {
		php_register_variable_safe(header->buf, content->buf, new_val_len, track_vars_array TSRMLS_CC);
	}
        
	return ret_ok;
}
*/

static void
php_cherokee_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
{
    cherokee_handler_php_t *hdl = SG(server_context);
    cherokee_connection_t *conn = HANDLER_CONN(hdl);
    cherokee_list_t                   *i;
    int new_val_len;

/* TODO
    list_for_each (i, &cgi_props->system_env) {
    	env_item_t *env = (env_item_t *)i;
	if (sapi_module.input_filter(PARSE_SERVER, env->env.buf, env->val.buf, env->val.len, &new_val_len TSRMLS_CC)) {
		php_register_variable_safe(env->env.buf, env->val.buf, new_val_len, track_vars_array TSRMLS_CC);
	}
    }

    cherokee_header_foreach_unknown (&conn->header, foreach_header_add_unknown_variable, hdl);
*/
    /* todo build_basic_env */
    
    if (! cherokee_buffer_is_empty (&conn->userdir)) {
        cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
	cherokee_buffer_add_str    (&tmp, "/~");
	cherokee_buffer_add_buffer (&tmp, &conn->userdir);
	cherokee_buffer_add_buffer (&tmp, &conn->request);
        if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &tmp.buf, tmp.len, &new_val_len TSRMLS_CC)) {
		php_register_variable_safe("PHP_SELF", tmp.buf, new_val_len, track_vars_array TSRMLS_CC);
	}
	cherokee_buffer_mrproper(&tmp);
    } else {
         if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &conn->request.buf, conn->request.len, &new_val_len TSRMLS_CC)) {
		php_register_variable_safe("PHP_SELF",  conn->request.buf, new_val_len, track_vars_array TSRMLS_CC);
	}
    }
}

static void cherokee_cherokee_sapi_log_message(char *msg)
{
    cherokee_handler_t *hdl;
    TSRMLS_FETCH();

    hdl = SG(server_context);
/* TODO
 * LOG_ERROR (CHEROKEE_ERROR_HANDLER_PHP, msg);
 */
}

static time_t php_cherokee_sapi_get_request_time(TSRMLS_D)
{
	return cherokee_bogonow_now;
}

static sapi_module_struct cherokee_sapi_module = {
        "handler_php",
        "Cherokee Handler",

	php_cherokee_sapi_startup,                      /* startup */
	php_module_shutdown_wrapper,                    /* shutdown */

	NULL,                                           /* activate */
	NULL,                                           /* deactivate */

	php_cherokee_sapi_ub_write,                     /* unbuffered write */
	php_cherokee_sapi_flush,                        /* flush */
	NULL,                                           /* get uid */
	php_cherokee_sapi_getenv,                       /* getenv */

	php_error,                                      /* error handler */

	NULL,                                           /* header handler */
	php_cherokee_sapi_send_headers,                 /* send headers handler */
	NULL,                                           /* send header handler */

	php_cherokee_sapi_read_post,                    /* read POST data */
	php_cherokee_sapi_read_cookies,                 /* read Cookies */

	php_cherokee_sapi_register_variables,
	NULL,                                           /* Log message */
	php_cherokee_sapi_get_request_time,             /* Request Time */

	STANDARD_SAPI_MODULE_PROPERTIES
};
#endif


/* Plug-in initialization
*/
PLUGIN_INFO_HANDLER_EASIEST_INIT (php, http_all_methods);


/* Methods implementation
*/
	static ret_t 
props_free (cherokee_handler_php_props_t *props)
{
#ifdef PHP
	cherokee_sapi_module.shutdown(&cherokee_sapi_module);
	sapi_shutdown();
#ifdef ZTS
	tsrm_shutdown();
#endif
#endif
	cherokee_buffer_mrproper(&props->system_env);
	return cherokee_module_props_free_base (MODULE_PROPS(props));
}


	ret_t 
cherokee_handler_php_configure (cherokee_config_node_t *conf, cherokee_server_t *srv, cherokee_module_props_t **_props)
{
	cherokee_list_t                      *i;
	cherokee_handler_php_props_t *props;

	if (*_props == NULL) {
		CHEROKEE_NEW_STRUCT (n, handler_php_props);

		cherokee_module_props_init_base (MODULE_PROPS(n), 
				MODULE_PROPS_FREE(props_free));

		n->highlight_source = false;

		cherokee_buffer_init(&n->system_env);

		*_props = MODULE_PROPS(n);
	}

	props = PROP_PHP(*_props);

	cherokee_config_node_foreach (i, conf) {
		cherokee_config_node_t *subconf = CONFIG_NODE(i);

		if (equal_buf_str (&subconf->key, "highlight_source")) {
			props->highlight_source = atoi(subconf->val.buf);
		} else {
			PRINT_MSG ("ERROR: Handler file: Unknown key: '%s'\n", subconf->key.buf);
			return ret_error;
		}
	}
#ifdef PHP
#ifdef ZTS
	tsrm_startup(1, 1, 0, NULL);
#endif

	sapi_startup(&cherokee_sapi_module);
	php_cherokee_sapi_startup(&cherokee_sapi_module);
#endif
	return ret_ok;
}

ret_t
cherokee_handler_php_new  (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
{
	ret_t ret;
	CHEROKEE_NEW_STRUCT (n, handler_php);
	
	/* Init the base class object
	 */
	cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(php));
	   
	MODULE(n)->init         = (handler_func_init_t) cherokee_handler_php_init;
	MODULE(n)->free         = (module_func_free_t) cherokee_handler_php_free;
	HANDLER(n)->step        = (handler_func_step_t) cherokee_handler_php_step;
	HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_php_add_headers;

	HANDLER(n)->support = hsupport_length;

	/* Init
	 */
	ret = cherokee_buffer_init (&n->header_buffer);
	if (unlikely(ret != ret_ok)) 
		return ret;
	
	ret = cherokee_buffer_ensure_size (&n->header_buffer, 1024);
	if (unlikely(ret != ret_ok)) 
		return ret;

	ret = cherokee_buffer_init (&n->output_buffer);
	if (unlikely(ret != ret_ok)) 
		return ret;

	ret = cherokee_buffer_ensure_size (&n->output_buffer, 8*1024);
	if (unlikely(ret != ret_ok)) 
		return ret;

	*hdl = HANDLER(n);
	return ret_ok;
}


ret_t 
cherokee_handler_php_free (cherokee_handler_php_t *hdl)
{
	cherokee_buffer_mrproper (&hdl->output_buffer);
	cherokee_buffer_mrproper (&hdl->header_buffer);

	return ret_ok;
}

ret_t 
cherokee_handler_php_init (cherokee_handler_php_t *hdl)
{
	cherokee_connection_t *conn = HANDLER_CONN(hdl);
	cherokee_buffer_add_buffer (&conn->local_directory, &conn->request);
#ifdef PHP
	TSRMLS_FETCH();

	SG(server_context) = hdl;
	SG(sapi_headers).http_response_code = http_ok;
	if (conn->query_string.len > 0)
		SG(request_info).query_string = strdup(conn->query_string.buf);
	SG(request_info).content_length = conn->range_end - conn->range_start + 1;

	hdl->phase = creating_page;
zend_first_try {
	if (php_request_startup(TSRMLS_C) != SUCCESS)
		zend_bailout();
	
	if (HDL_PHP_PROPS(hdl)->highlight_source) {
		zend_syntax_highlighter_ini syntax_highlighter_ini;
		php_get_highlight_struct(&syntax_highlighter_ini);
		highlight_file(conn->local_directory.buf, &syntax_highlighter_ini TSRMLS_CC);
	} else {
		zend_file_handle zfd;

		zfd.type = ZEND_HANDLE_FILENAME;
		zfd.filename = conn->local_directory.buf;
		zfd.free_filename = 0;
		zfd.opened_path = NULL;

		php_execute_script(&zfd TSRMLS_CC);
	}
} zend_end_try();
	php_request_shutdown(NULL);
#endif
	hdl->phase = script_done;

	return ret_ok;
}


ret_t 
cherokee_handler_php_step (cherokee_handler_php_t *hdl, cherokee_buffer_t *buffer)
{
	switch (hdl->phase) {
		case creating_page:
			return ret_eagain;
		case script_flushing:
			cherokee_buffer_add_buffer (buffer, &hdl->output_buffer);
			cherokee_buffer_clean (&hdl->output_buffer);
			return ret_ok;
		case script_done:
			cherokee_buffer_add_buffer (buffer, &hdl->output_buffer);
			return ret_eof_have_data;
		default:
			SHOULDNT_HAPPEN;
	}

	return ret_error;
}


ret_t 
cherokee_handler_php_add_headers (cherokee_handler_php_t *hdl, cherokee_buffer_t *buffer)
{
	cherokee_buffer_add_buffer (buffer, &hdl->header_buffer);
	return ret_ok;
}



