#include "mod_core.h"
#include "ap_config.h"
#include "http_log.h"

#include "http_config.h"
#include "scoreboard.h"
#include "ap_mpm.h"
#include "apr_lib.h"

#define MAX_COMMAND_LEN	100
#define MAX_HDR_LEN	1024
#define MAX_READ_LINE	2048

#define WELCOME	"220 apache ESMTP\r\n"

#define SMTP_COMMAND_QUIT		100
#define SMTP_COMMAND_QUIT_MSG		"QUIT"

#define SMTP_COMMAND_EHLO		101
#define SMTP_COMMAND_EHLO_MSG		"EHLO"

#define SMTP_COMMAND_MAIL_FROM		102
#define SMTP_COMMAND_MAIL_FROM_MSG	"MAIL FROM:"

#define SMTP_COMMAND_RCPT_TO          	103
#define SMTP_COMMAND_RCPT_TO_MSG      	"RCPT TO:"

#define SMTP_COMMAND_DATA	        104
#define SMTP_COMMAND_DATA_MSG        	"DATA"


#define SMTP_COMMAND_OK			200
#define SMTP_COMMAND_NOTIMPLEMENTED	300

#define SMTP_COMMAND_END		400

#define SMTP_OK				250


#define HELO_ANSWER	"250 localhost Hello localhost [127.0.0.1], please to meet you\r\n"
#define START_DATA	"354 Enter mail, end with \".\" on a line by itself\r\n"

int send_message_ok(request_rec *r)
{
	conn_rec *c = r->connection;
        apr_bucket *e;
        apr_bucket_brigade *bb;
	char *answer = NULL;

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Processing End message");


	answer = apr_psprintf(r->pool, "250 PAAAAAA message accepted for delivery\r\n");
	bb = apr_brigade_create(r->pool, c->bucket_alloc);
	e = apr_bucket_immortal_create(answer, strlen(answer), c->bucket_alloc);
	APR_BRIGADE_INSERT_TAIL(bb, e);
        e = apr_bucket_flush_create(c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(bb, e);
        ap_pass_brigade(c->output_filters, bb);


	return OK;
}


int get_simple_body(request_rec *r, char **body)
{
	int read_bytes = 0;
	int len = 0;
	char *global_data = NULL;
	int seen_end = 0;
	apr_status_t rv;

	apr_bucket *e;
        apr_bucket_brigade *bb;
	char *tmp;
	int num_brigade = 0;

	bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);

	do {
		int num_bucket = 0;

		num_brigade++;
		rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
				
		if (rv != APR_SUCCESS) {
			seen_end = 1;
			ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server , "No more data or error reading");
			break;
		}

		if (APR_BRIGADE_EMPTY(bb)) {
			seen_end = 1;
			ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Brigade %i vide", num_brigade);
			break;
		}

		APR_BRIGADE_FOREACH(e, bb) {
			const char *str;
		        apr_size_t len;
			char *ptr = NULL;

			if (APR_BUCKET_IS_EOS(e)) {
				seen_end = 1;
				break;
			}
		
			rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
			if (rv != APR_SUCCESS) {
				seen_end = 1;
				ap_log_error(APLOG_MARK, APLOG_DEBUG, rv , r->server , "Error reading bucket %i brigade %i", num_bucket, num_brigade);
				break;
			}

			if (len <= 0) {
				num_bucket++;
				continue;
			}

			if (!str) {
				num_bucket++;
				continue; 
			}

			tmp = realloc(global_data, read_bytes + len);
			global_data = tmp;
			memcpy(global_data + read_bytes, str, len);
			read_bytes += len;

			if ((ptr = strstr(str, "\r\n.\r\n"))) {
				ap_log_error(APLOG_MARK, APLOG_DEBUG, rv , r->server , "End in bucket %i brigade %i", num_bucket, num_brigade);
				seen_end = 1;
				break;
			}
			num_bucket++;
		}
		apr_brigade_cleanup(bb);	
	} while (seen_end == 0);


	tmp = realloc(global_data, read_bytes + 1);
	global_data = tmp;
	global_data[read_bytes] = 0;

	if (!body) {
		ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Unable to return body");
		return read_bytes;
	}

	*body = (char *)apr_pcalloc(r->pool, read_bytes + 1);
	memcpy (*body, global_data, read_bytes + 1); 
	free(global_data);

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Message is %i bytes", read_bytes);

	return read_bytes;
}

int smtp_read_header(request_rec *r)
{
	int num_hdr = 0;
	int hdr_len = 0;
	char hdr[MAX_HDR_LEN];


	while ((hdr_len = ap_getline(hdr, MAX_HDR_LEN, r, 1)) > 0) {
		
		char *hdr_name = NULL;
		char *hdr_value = NULL;
		char *hdr_sep = NULL;

		if (!(hdr_sep = strchr(hdr, ':'))) {
			ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Received a malformed header: %s", hdr);
			break;
		}

		*hdr_sep = '\0';
		++hdr_sep;
		
		hdr_name = apr_pstrndup(r->pool, hdr, hdr_sep - hdr);
		hdr_value = apr_pstrdup(r->pool, hdr_sep);
		if (apr_isspace(*hdr_value)) ++hdr_value;
		apr_table_add(r->headers_in, hdr_name, hdr_value);
		ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Parsed header name: %s value: %s", hdr_name, hdr_value);
		num_hdr++;
	}


	return num_hdr;
}


int command_data(request_rec *r, char *arg)
{

	conn_rec *c = r->connection;
        apr_bucket *e;
        apr_bucket_brigade *bb;
	char *answer = NULL;		
	char *email = NULL;

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Processing command DATA");

	bb = apr_brigade_create(r->pool, c->bucket_alloc);
	e = apr_bucket_immortal_create(START_DATA, strlen(START_DATA), c->bucket_alloc);
	APR_BRIGADE_INSERT_TAIL(bb, e);
        e = apr_bucket_flush_create(c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(bb, e);
        ap_pass_brigade(c->output_filters, bb);


	return OK;
}

int command_mailfrom(request_rec *r, char *arg)
{

	/* here, sender is arg */

	conn_rec *c = r->connection;
        apr_bucket *e;
        apr_bucket_brigade *mailfrom_bb;
	char *answer = NULL;		
	char *email = NULL;
	char *from_user = NULL;
	char *from_domain = NULL;

	while (*arg == ' ') arg++;

	from_user = get_email_user(r, arg);
	from_domain = get_email_domain(r, arg);

	if (!from_user) {
		/* send here error code for incorrect mail from */

	}

	if (!from_domain) {

		/* send here error code for incorrect mail from */

	}

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "From user: %s domain: %s", from_user, from_domain);

	answer = apr_psprintf(r->pool, "%i %s... Sender OK\r\n", SMTP_OK, arg);
	mailfrom_bb = apr_brigade_create(r->pool, c->bucket_alloc);
	e = apr_bucket_immortal_create(answer, strlen(answer), c->bucket_alloc);
	APR_BRIGADE_INSERT_TAIL(mailfrom_bb, e);
        e = apr_bucket_flush_create(c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(mailfrom_bb, e);
        ap_pass_brigade(c->output_filters, mailfrom_bb);


	return OK;
}

int command_rcptto(request_rec *r, char *arg)
{

	conn_rec *c = r->connection;
        apr_bucket *e;
        apr_bucket_brigade *bb;
	char *answer = NULL;		
	char *email = NULL;

	r->user = get_email_user(r, arg);
	r->hostname = get_email_domain(r, arg);

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Email for user: %s domain: %s", r->user, r->hostname);
	
	answer = apr_psprintf(r->pool, "%i %s... Recipient OK\r\n", SMTP_OK, arg);
	bb = apr_brigade_create(r->pool, c->bucket_alloc);
	e = apr_bucket_immortal_create(answer, strlen(answer), c->bucket_alloc);
	APR_BRIGADE_INSERT_TAIL(bb, e);
        e = apr_bucket_flush_create(c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(bb, e);
        ap_pass_brigade(c->output_filters, bb);


	return OK;
}


int command_ehlo(request_rec *r, char *arg)
{
        conn_rec *c = r->connection;
        apr_bucket *e;
        apr_bucket_brigade *helo_bb;

        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Processing command EHLO");
	
	helo_bb = apr_brigade_create(r->pool, c->bucket_alloc);

        e = apr_bucket_immortal_create(HELO_ANSWER, strlen(HELO_ANSWER), c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(helo_bb, e);
        e = apr_bucket_flush_create(c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(helo_bb, e);
	ap_pass_brigade(c->output_filters, helo_bb);

	return OK;
}


int handle_command(request_rec *r)
{
	char command[100];
	int len = 0;

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Waiting command...");

	if ((len = ap_getline(command, MAX_COMMAND_LEN, r, 0)) <= 0)
		return SMTP_COMMAND_QUIT;

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Received %i bytes", len);

	if (!command) {
		ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "No command received");
		return SMTP_COMMAND_QUIT;
	}
	else {
	        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Received command %s", command);
	}

	if (strncmp(command, SMTP_COMMAND_EHLO_MSG, strlen(SMTP_COMMAND_EHLO_MSG)) == 0) {
		command_ehlo(r, command + strlen(SMTP_COMMAND_EHLO_MSG));
		return SMTP_COMMAND_OK;
	}

	if (strncmp(command, SMTP_COMMAND_MAIL_FROM_MSG, strlen(SMTP_COMMAND_MAIL_FROM_MSG)) == 0) {
		command_mailfrom(r, command + strlen(SMTP_COMMAND_MAIL_FROM_MSG));
		return SMTP_COMMAND_OK;
	}
	
	if (strncmp(command, SMTP_COMMAND_RCPT_TO_MSG, strlen(SMTP_COMMAND_RCPT_TO_MSG)) == 0) {
		command_rcptto(r, command + strlen(SMTP_COMMAND_RCPT_TO_MSG));
		return SMTP_COMMAND_OK;
	}

	if (strncmp(command, SMTP_COMMAND_DATA_MSG, strlen(SMTP_COMMAND_DATA_MSG)) == 0) {
		command_data(r, command + strlen(SMTP_COMMAND_DATA_MSG));
		ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Now parsing the headers");
		smtp_read_header(r);
		return SMTP_COMMAND_END;
	}



 	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server , "Command %s - Not implemented", command);	

	return SMTP_COMMAND_NOTIMPLEMENTED;
}


int init_request(conn_rec *c, request_rec *r)
{

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server , "Create request struct");
        r->connection = c;
        r->server = c->base_server;

        c->keepalive = 1;

        r->user = NULL;
        r->ap_auth_type = NULL;

        //r->allowed_methods = ap_make_method_list(p, 2);

        r->headers_in = apr_table_make(r->pool, 1);
        r->subprocess_env = apr_table_make(r->pool, 1);
        r->headers_out = apr_table_make(r->pool, 1);
        r->err_headers_out = apr_table_make(r->pool, 1);
        r->notes = apr_table_make(r->pool, 5);
        
	r->request_config = ap_create_request_config(r->pool);
        ap_run_create_request(r);

        r->per_dir_config = r->server->lookup_defaults;
        r->sent_bodyct = 0;
        r->output_filters = c->output_filters;
        r->input_filters = c->input_filters;

        r->status = HTTP_OK;

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server , "End create request struct");

	return OK;
}

int send_hello(request_rec *r)
{
	conn_rec *c = r->connection;

        apr_bucket *e;
	apr_bucket_brigade *helo_bb;

	helo_bb = apr_brigade_create(r->pool, c->bucket_alloc);
	
	e = apr_bucket_immortal_create(WELCOME, strlen(WELCOME), c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(helo_bb, e);
        e = apr_bucket_flush_create(c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(helo_bb, e);
        ap_pass_brigade(c->output_filters, helo_bb);
						
	return OK;
}

static int ap_process_smtp_connection(conn_rec *c)
{
	apr_pool_t *p;
	request_rec *r;
    	server_rec *s = c->base_server;
	int csd_set = 0;
        apr_socket_t *csd = NULL;
	apr_bucket_brigade *tmp_bb;
	char *body;

	char *command;

	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s , "I get the connection here");

	apr_pool_create(&p, c->pool);
	r = apr_pcalloc(p, sizeof(*r));
	r->pool = p;
	
	init_request(c, r);
	tmp_bb = apr_brigade_create(r->pool, c->bucket_alloc);	
	
	ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
		
	/* first send helo */

	send_hello(r);
	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s , "Send welcome: %s", WELCOME);
	
	
	while (handle_command(r) == SMTP_COMMAND_OK) {
		ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s , "End processing command");
	}


	get_simple_body(r, &body);
	
	send_message_ok(r);
	
	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s , "End smtp connect hook");
	apr_pool_destroy(p);

	return OK;
}

static void register_hooks(apr_pool_t *p)
{
	ap_hook_process_connection(ap_process_smtp_connection,NULL,NULL, APR_HOOK_MIDDLE);




}

module AP_MODULE_DECLARE_DATA smtp_module = {
STANDARD20_MODULE_STUFF,
NULL,                        /* create per-directory config structure */
NULL,                        /* merge per-directory config structures */
NULL,                        /* create per-server config structure */
NULL,                        /* merge per-server config structures */
NULL,           	     /* command apr_table_t */
register_hooks,	             /* register hooks */
};
			    
