/* Copyright 2013 Akira Ohta (akohta001@gmail.com)
    This file is part of ntch.

    The ntch is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    The ntch 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 ntch.  If not, see <http://www.gnu.org/licenses/>.
    
*/
#include	<sys/types.h> 
#include	<sys/socket.h>
#include	<sys/stat.h>
#include	<fcntl.h>
#include	<arpa/inet.h>
#include	<string.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<openssl/sha.h>
#include	<assert.h>

#include	"env.h"
#include	"utils/nt_std_t.h"
#include	"utils/text.h"
#include	"utils/db.h"
#include	"utils/nt_mutex.h"
#include	"net/nt_socket.h"
#include	"net/nt_http.h"

static int add_header(nt_http_response_header_tp, const char *line,
			nt_cookie_tp cookiep, const char *host);
static int parse_status_code(const char *source);


BOOL nt_http_save_response_header(const char *out_path, 
			nt_http_response_header_tp responsep)
{
	char key[DB_KEY_SIZE_MAX];
	nt_db_log_rec_t rec;
	datum d_key, d_data;
	int result;
	const char *log_path;
	DBM *dbm;
	nt_mutex_handle h_mutex;

	memset(key, '\0', sizeof(key));
	memset(&d_key, '\0', sizeof(datum));
	memset(&d_data, '\0', sizeof(datum));
	memset(&rec, '\0', sizeof(nt_db_log_rec_t));

	assert(out_path != NULL);
	assert(responsep != NULL);

	if(!responsep->last_modified)
		return TRUE;

	if(!nt_db_cpy_key(out_path, key)){
		return FALSE;
	}

	rec.content_length = responsep->content_length;
	strcpy(rec.last_modified, responsep->last_modified);
	log_path = nt_db_get_log_path();
	
	h_mutex = nt_mutex_get_one_time_handle(NT_MUTEX_ROOT_NAME_FILE);
	if(!h_mutex){
		return FALSE;
	}
	if(!nt_mutex_add_moniker(h_mutex, LOG_DB_W_NAME)){
		return FALSE;
	}
	if(!nt_mutex_lock(h_mutex)){
		return FALSE;
	}
	
	dbm = dbm_open((char*)log_path, O_RDWR | O_CREAT, 0600);
	if(dbm == NULL){
		nt_mutex_unlock(h_mutex);
		return FALSE;
	}

	d_key.dsize = strlen(key);
	d_key.dptr = (void*)key;
	d_data.dsize = sizeof(nt_db_log_rec_t);
	d_data.dptr = (void*)&rec;

	result = dbm_store(dbm, d_key, d_data, DBM_REPLACE);
	if(result != 0){
		dbm_close(dbm);
		nt_mutex_unlock(h_mutex);
		return FALSE;
	}

	dbm_close(dbm);
	nt_mutex_unlock(h_mutex);
	return TRUE;
}

void nt_http_free_response_header(nt_http_response_header_tp responsep)
{
	if(responsep->status_text)
		free(responsep->status_text);
	if(responsep->last_modified)
		free(responsep->last_modified);
	free(responsep);
}

nt_http_response_header_tp nt_http_alloc_response_header()
{
	nt_http_response_header_tp ptr;
	ptr = calloc(1, sizeof(nt_http_response_header_t));
	if(ptr == NULL)
		return NULL;
	return ptr;
}

int nt_http_parse_response_header(int readfd, 
		nt_http_response_header_tp responsep,
		nt_cookie_tp cookiep, const char *host)
{
	assert(responsep != NULL);

	char buf[1024];
	char c;
	int idx = 0, state = 0;
	int  nread;

	do{
		nread = read(readfd, &c, 1);
		switch(nread){
		case -1:
			return -1;
		case 0:
			return 0;
		case 1:
			switch(c){
			case '\r':
				state = 1;
				buf[idx] = '\0';
				break;
			case '\n':
				assert(state == 1);
				state = 0;
				buf[idx] = '\0';
				if(0 == add_header(responsep, buf,
							cookiep, host)){
					return 1;
				}
				idx = 0;
				break;
			default:
				state = 0;
				buf[idx] = c;
				idx++;
				if(sizeof(buf) == idx)
					return -1;
				break;
			}/* switch 2 */
			break;
		default:
			return -1;
		}/* switch */
	}while(1);
	return 1;
}


int nt_http_parse_response_header2(
		const char *data, size_t data_len, 
		nt_http_response_header_tp responsep)
{
	assert(responsep != NULL);

	char buf[256];
	char c;
	int i;
	int idx = 0, state = 0;

	for(i = 0; i < data_len; i++){
		c = data[i];
		switch(c){
		case '\r':
			state = 1;
			buf[idx] = '\0';
			break;
		case '\n':
			assert(state == 1);
			state = 0;
			buf[idx] = '\0';
			if(0 == add_header(responsep, buf, NULL, NULL)){
				return i+1;
			}
			idx = 0;
			break;
		default:
			state = 0;
			buf[idx] = c;
			idx++;
			if(sizeof(buf) == idx)
				return -1;
			break;
		}/* switch */
	}/* end for */
	return i;
}

static int add_header(nt_http_response_header_tp headerp, 	
			const char *line, nt_cookie_tp cookiep, const char *host)
{
	char *cptr;
	int len, num;
	len = strlen(line);
	if(len == 0)
		return 0;
	if(0 == strncmp("HTTP/", line, 5)){
		headerp->status_code = parse_status_code(line);
		if(headerp->status_code < 0)
			return -1;
		cptr = nt_trim(line);
		if(cptr == NULL)
			return -1;
		headerp->status_text = cptr;
	}else if(0 == strncmp("Last-Modified:", line, 14)){
		cptr = nt_trim(line+14);
		if(cptr == NULL)
			return -1;
		headerp->last_modified = cptr;
	}else if(0 == strncmp("Content-Length:", line, 15)){
		num = strtol(line+15, &cptr, 10);
		if((line+17) >= cptr)
			return -1;
		headerp->content_length = num;
	}else if(0 == strncmp("Content-Type:", line, 13)){
	}else if(0 == strncmp("Content-Encoding:", line, 17)){
		cptr = strstr(line+17, "gzip");
		if(cptr){
			SET_FLAG(headerp, GZIP_FLAG);
		}
	}else if(0 == strncmp("Transfer-Encoding:", line, 18)){
		cptr = strstr(line+18, "chunked");
		if(cptr){
			SET_FLAG(headerp, CHUNKED_FLAG);
		}
	}else if(0 == strncmp("Set-Cookie:", line, 11)){
		if(!cookiep || !host)
			return -1;
		if(!nt_add_cookie(cookiep, host, line+11))
			return -1;
	}
	return len;
}

int parse_status_code(const char *source)
{
	int len = strlen(source);
	int i ,num;
	int status = 0;
	const char *startptr;
	char *endptr;

	for(i = 0; i < len; i++){
		switch(source[i]){
		case ' ':
			startptr = source+i+1;
			num = strtol(startptr, &endptr, 10);
			if(startptr >= endptr)
				return -1;
			return num;
		default:
			if(status == 1)
				status = 2;
			break;
		} /* end switch */
	}/* end for */

	return -1;
}
