#ifndef NOLUA 
/* 2006-11-18 nsock buffering by Marek Majkowski */

#include "nse_nsock.h"
#include "nse_auxiliar.h"
#include "nse_macros.h"

#include "nsock.h"
#include "nmap_error.h"
#include "osscan.h"
#include "NmapOps.h"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sstream>
#include <iomanip>

#include <map>


#if HAVE_OPENSSL
#include <openssl/ssl.h>
#endif

#define SCRIPT_ENGINE			"SCRIPT ENGINE"
#define NSOCK_WRAPPER			"NSOCK WRAPPER"
#define NSOCK_WRAPPER_SUCCESS		0 
#define NSOCK_WRAPPER_ERROR		2 

#define FROM 	1
#define TO 	2

#define DEFAULT_TIMEOUT 30000

extern NmapOps o;

// defined in nse_main.cc but also declared here
// to keep the .h files clean
int process_waiting2running(lua_State* l, int resume_arguments);

static int l_nsock_connect(lua_State* l);
static int l_nsock_send(lua_State* l);
static int l_nsock_receive(lua_State* l);
static int l_nsock_receive_lines(lua_State* l);
static int l_nsock_receive_bytes(lua_State* l);
static int l_nsock_get_info(lua_State* l);
static int l_nsock_close(lua_State* l);
static int l_nsock_set_timeout(lua_State* l);
static int l_nsockbuf_receive_bytes(lua_State* l);
static int l_nsockbuf_receive_line(lua_State* l);


void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
void l_nsockbuf_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state);

int l_nsock_checkstatus(lua_State* l, nsock_event nse);

void l_nsock_trace(nsock_iod nsiod, char* message, int direction);
char* l_nsock_hexify(const void* data, size_t data_len);
char* inet_ntop_both(int af, const void* v_addr, char* ipstring);
unsigned short inet_port_both(int af, const void* v_addr);

static luaL_reg l_nsock [] = {
	{"connect", l_nsock_connect},
	{"send", l_nsock_send},
	{"receive", l_nsock_receive},
	{"receive_lines", l_nsock_receive_lines},
	{"receive_bytes", l_nsock_receive_bytes},
	{"get_info", l_nsock_get_info},
	{"close", l_nsock_close},
	{"set_timeout", l_nsock_set_timeout},
	{"buf_receive_line",  l_nsockbuf_receive_line},
	{"buf_receive_bytes", l_nsockbuf_receive_bytes},
	{NULL, NULL}
};

static nsock_pool nsp;

struct l_nsock_udata {
	int timeout;
	nsock_iod nsiod;
	void *ssl_session;
};

int l_nsock_open(lua_State* l) {
	auxiliar_newclass(l, "nsock", l_nsock);

        nsp = nsp_new(NULL);
	nsp_settrace(nsp, o.debugging, o.getStartTime());

	return NSOCK_WRAPPER_SUCCESS;
}

int l_nsock_new(lua_State* l) {
	struct l_nsock_udata* udata;
	udata = (struct l_nsock_udata*) lua_newuserdata(l, sizeof(struct l_nsock_udata));
	auxiliar_setclass(l, "nsock", -1);
	udata->nsiod = NULL;
	udata->ssl_session = NULL;
	udata->timeout = DEFAULT_TIMEOUT;
	
	return 1;
}

int l_nsock_loop(int tout) {
	return nsock_loop(nsp, tout);
}

int l_nsock_checkstatus(lua_State* l, nsock_event nse) {
	enum nse_status status = nse_status(nse);

	switch (status) {
		case NSE_STATUS_SUCCESS:
			lua_pushboolean(l, true);
			return NSOCK_WRAPPER_SUCCESS;
			break;
		case NSE_STATUS_ERROR:
		case NSE_STATUS_TIMEOUT:
		case NSE_STATUS_CANCELLED:
		case NSE_STATUS_KILL:
		case NSE_STATUS_EOF:
			lua_pushnil(l);
			lua_pushstring(l, nse_status2str(status));
			return NSOCK_WRAPPER_ERROR;
			break;
		case NSE_STATUS_NONE:
		default:
			fatal("%s: In: %s:%i This should never happen.", 
					NSOCK_WRAPPER, __FILE__, __LINE__);
			break;
		
	}

	return -1;
}

static int l_nsock_connect(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);
	const char* addr = luaL_checkstring(l, 2);
	unsigned short port = (unsigned short) luaL_checkint(l, 3);
	const char *how = luaL_optstring(l, 4, "tcp");

	const char* error;
	struct addrinfo *dest;
	int error_id;

	error_id = getaddrinfo(addr, NULL, NULL, &dest);
	if (error_id) {
		error = gai_strerror(error_id);
		lua_pushboolean(l, false);
		lua_pushstring(l, error);
		return 2;
	}

	udata->nsiod = nsi_new(nsp, NULL);

	switch (how[0]) {
		case 't':
			if (strcmp(how, "tcp")) goto error;
			nsock_connect_tcp(nsp, udata->nsiod, l_nsock_connect_handler, 
					udata->timeout, l, dest->ai_addr, dest->ai_addrlen, port);
			break;
		case 'u':
			if (strcmp(how, "udp")) goto error;
			nsock_connect_udp(nsp, udata->nsiod, l_nsock_connect_handler, 
					l, dest->ai_addr, dest->ai_addrlen, port);
			break;
		case 's':
			if (strcmp(how, "ssl")) goto error;
#ifdef HAVE_OPENSSL
			nsock_connect_ssl(nsp, udata->nsiod, l_nsock_connect_handler, 
					udata->timeout, l, dest->ai_addr, dest->ai_addrlen, port, 
					udata->ssl_session);
			break;
#else
			luaL_argerror(l, 4, "Sorry, you don't have openssl.");
			return 0;
#endif
		default:
			goto error;
			break;
	}

	freeaddrinfo(dest);
	return lua_yield(l, 0);

error:
	freeaddrinfo(dest);
	luaL_argerror(l, 4, "invalid connection method");
	return 0;
}

void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
	lua_State* l = (lua_State*) lua_state;

	if(o.scripttrace) {
		l_nsock_trace(nse_iod(nse), "CONNECT", TO);
	}

	if(l_nsock_checkstatus(l, nse) == NSOCK_WRAPPER_SUCCESS) {
		process_waiting2running((lua_State*) lua_state, 1);
	} else {
		process_waiting2running((lua_State*) lua_state, 2);
	}
}

static int l_nsock_send(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);
	const char* string = luaL_checkstring(l, 2);
	size_t string_len = lua_objlen (l, 2);
	char* hexified;

	if(udata->nsiod == NULL) {
		lua_pushboolean(l, false);
		lua_pushstring(l, "Trying to send through a closed socket\n");
		return 2;	
	}

	if(o.scripttrace) {
		hexified = l_nsock_hexify((const void*)string, string_len);
		l_nsock_trace(udata->nsiod, hexified, TO);
		free(hexified);
	}

	nsock_write(nsp, udata->nsiod, l_nsock_send_handler, udata->timeout, l, string, string_len);
	return lua_yield(l, 0);
}

void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
	lua_State* l = (lua_State*) lua_state;
	
	if(l_nsock_checkstatus(l, nse) == NSOCK_WRAPPER_SUCCESS) {
		process_waiting2running((lua_State*) lua_state, 1);
	} else {
		process_waiting2running((lua_State*) lua_state, 2);
	}
}

static int l_nsock_receive(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);

	if(udata->nsiod == NULL) {
		lua_pushboolean(l, false);
		lua_pushstring(l, "Trying to receive through a closed socket\n");
		return 2;	
	}

	nsock_read(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, l);

	return lua_yield(l, 0);
}

static int l_nsock_receive_lines(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);
	int nlines = (int) luaL_checknumber(l, 2);

	if(udata->nsiod == NULL) {
		lua_pushboolean(l, false);
		lua_pushstring(l, "Trying to receive lines through a closed socket\n");
		return 2;	
	}

	nsock_readlines(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, l, nlines);

	return lua_yield(l, 0);
}

static int l_nsock_receive_bytes(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);
	int nbytes = (int) luaL_checknumber(l, 2);

	if(udata->nsiod == NULL) {
		lua_pushboolean(l, false);
		lua_pushstring(l, "Trying to receive bytes through a closed socket\n");
		return 2;	
	}

	nsock_readbytes(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, l, nbytes);

	return lua_yield(l, 0);
}

void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
	lua_State* l = (lua_State*) lua_state;
	char* rcvd_string;
	int rcvd_len = 0;
	char* hexified;

	if(l_nsock_checkstatus(l, nse) == NSOCK_WRAPPER_SUCCESS) {
		rcvd_string = nse_readbuf(nse, &rcvd_len);

		if(o.scripttrace) {
			hexified = l_nsock_hexify((const void*) rcvd_string, (size_t) rcvd_len);
			l_nsock_trace(nse_iod(nse), hexified, FROM);
			free(hexified);
		}

		lua_pushlstring(l, rcvd_string, rcvd_len);
		process_waiting2running((lua_State*) lua_state, 2);
	} else {
		process_waiting2running((lua_State*) lua_state, 2);
	}
}

void l_nsock_trace(nsock_iod nsiod, char* message, int direction) { 
	int status; 
	int protocol; 
	int af; 
	struct sockaddr local; 
	struct sockaddr remote; 
	char* ipstring_local = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);
	char* ipstring_remote = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);

	status =  nsi_getlastcommunicationinfo(nsiod, &protocol, &af,
			&local, &remote, sizeof(sockaddr)); 

	log_write(LOG_STDOUT, "SCRIPT ENGINE: %s %s:%d %s %s:%d | %s\n", 
			(protocol == IPPROTO_TCP)? "TCP" : "UDP",
			inet_ntop_both(af, &local, ipstring_local), 
			inet_port_both(af, &local), 
			(direction == TO)? ">" : "<", 
			inet_ntop_both(af, &remote, ipstring_remote), 
			inet_port_both(af, &remote), 
			message); 

	free(ipstring_local);
	free(ipstring_remote);
}

char* l_nsock_hexify(const void *data, size_t data_len) { 
	std::ostringstream osDump; 
	std::ostringstream osNums; 
	std::ostringstream osChars; 

	const unsigned char* c_data = (const unsigned char*) data;

	unsigned long i; 
	unsigned int width = 16;
	unsigned long printable_chars = 0;

	// if more than 95% of all characters are printable, we don't hexify
	for(i = 0; i < data_len; i++) {
		if(c_data[i] > 31 && c_data[i] < 127)
			printable_chars++;
	}

	if((double)printable_chars > (double)data_len * 95.0 / 100.0) {
		char* result = (char*) safe_malloc((data_len+1)*sizeof(char));
		memcpy(result, data, data_len);	
		result[data_len] = '\0';
		return result;
	}
		
	osDump << std::endl;
	for(i = 0; i < data_len; i++) 
	{ 
		if(i < data_len) 
		{ 
			char c = c_data[i]; 
			unsigned short n = (unsigned short)c_data[i]; 
			osNums << std::setbase(16) << std::setw(2) << std::setfill('0') << n << " "; 
			osChars << ((n < 32) || (n > 126) ? '.' : c); 
		} 
		if(((i % width) == width - 1) || ((i == data_len) && (osNums.str().size() > 0))) 
		{ 
			osDump 	<< std::setbase(16) 
				<< std::setw(8) 
				<< std::setfill('0') 
				<< (i - (i % width)) << ": " 
				<< std::setfill(' ') 
				<< std::setiosflags(std::ios_base::left) 
				<< std::setw(3 * width) 
				<< osNums.str() 
				<< osChars.str() 
				<< std::resetiosflags(std::ios_base::left) 
				<< std::endl; 
			osNums.str(""); 
			osChars.str(""); 
		} 
	} 

	return strdup(osDump.str().c_str());
}

char* inet_ntop_both(int af, const void* v_addr, char* ipstring) {
//	char* ipstring = (char*) malloc(sizeof(char) * INET6_ADDRSTRLEN);

	if(af == AF_INET) {
		inet_ntop(AF_INET, &((struct sockaddr_in*) v_addr)->sin_addr, 
				ipstring, INET6_ADDRSTRLEN);

		return ipstring;
	} 
#ifdef HAVE_IPV6
	else if(af == AF_INET6) {
		inet_ntop(AF_INET6, &((struct sockaddr_in6*) v_addr)->sin6_addr, 
				ipstring, INET6_ADDRSTRLEN);
		return ipstring;
	} 
#endif
	else {
		return "unknown protocol";
	}

}

unsigned short inet_port_both(int af, const void* v_addr) {
	int port;
	if(af == AF_INET) {
		port = ((struct sockaddr_in*) v_addr)->sin_port;	
	}
#ifdef HAVE_IPV6
	else if(af == AF_INET6) {
		port = ((struct sockaddr_in6*) v_addr)->sin6_port;	
	}
#endif
	else {
		port = 0;
	}
	
	return ntohs(port);
}

static int l_nsock_get_info(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);
	int status;

	int protocol; // tcp or udp
	int af; // address family
	struct sockaddr local;
	struct sockaddr remote;
	char* ipstring_local = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);
	char* ipstring_remote = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);

	if(udata->nsiod == NULL) {
		lua_pushboolean(l, false);
		lua_pushstring(l, "Trying to get info from a closed socket\n");
		return 2;	
	}

	status =  nsi_getlastcommunicationinfo(udata->nsiod, &protocol, &af,
			&local, &remote, sizeof(sockaddr));

	lua_pushboolean(l, true);

	lua_pushstring(l, inet_ntop_both(af, &local, ipstring_local));
	lua_pushnumber(l, inet_port_both(af, &local));

	lua_pushstring(l, inet_ntop_both(af, &remote, ipstring_remote));
	lua_pushnumber(l, inet_port_both(af, &remote));

	free(ipstring_local);
	free(ipstring_remote);
	return 5;
}

void nsockbuf_free(nsock_iod nsi); /* see nsockbuf docs in the bottom */

static int l_nsock_close(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);

	if(udata->nsiod == NULL) {
		lua_pushboolean(l, false);
		lua_pushstring(l, "Trying to close a closed socket\n");
		return 2;	
	}

	if(o.scripttrace) {
		l_nsock_trace(udata->nsiod, "CLOSE", TO);
	}

#ifdef HAVE_OPENSSL
	if (udata->ssl_session)
		SSL_SESSION_free((SSL_SESSION*)udata->ssl_session);
	udata->ssl_session=NULL;
#endif
	/* Free nsockbuf internals, just in case nsockbuf was used. */
	nsockbuf_free(udata->nsiod);

	nsi_delete(udata->nsiod, NSOCK_PENDING_NOTIFY);

	udata->nsiod = NULL;

	lua_pushboolean(l, true);
	return 1;
}

static int l_nsock_set_timeout(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);
	int timeout = (unsigned short) luaL_checkint(l, 2);

	udata->timeout = timeout;

	return 0;
}



/************* NSOCKBUF *****************/
/*****************************************************************************/
/* Using normal functions of nsock library you can't receive
   exacly one line in result. This functions allow you to do this :)
   They buffer results and return them in parts you requested.

   It is for you when you need to iterate through lines on
   data received from nsock.
   Or if you must receive exacly specified amount of data that can
   be send by host in more than one packet.

   Using this features is of course slower than using normal nsock, but
   this wrapper really makes programing nsock easier.

   TODO: This implementation uses 'memcpy'. It is possible to
   create implementation without copying the same data many times.
   Maybe I'll code it in the future.
   TODO: Oh yes. This implementation uses <maps>. It's not
   'the fastest possible way'. Integrating this code to real nsock
   will make this code even faster, because we could ommit searches
   in <maps>. Question to Fyodor: maybe we should put this code into nsock?
*/

/* Request exacly one line from this NSI. Other parameters are like those in nsock */
nsock_event_id nsockbuf_readline(nsock_pool nsp, nsock_iod nsi,
                                 nsock_ev_handler handler, int timeout_msecs,
                                 void *userdata);

/* Request exacly NBYTES bytes from this socket. */
nsock_event_id nsockbuf_readbytes(nsock_pool nsp, nsock_iod nsi,
                                  nsock_ev_handler handler, int timeout_msecs,
                                  void *userdata, int nbytes);

/* Receive previusly requested data. Like nsock_readbuf().
   You should not modify received string. It is null terminated,
   but it also can contain zero's inside. This string will be 
   freed automatically when you'll do nsockbuf_read* again. */
char *nsockbuf_readbuf(nsock_event nse, int *nbytes);

/* Is internal buffer of nsockbuf empty? Is there some data buffered? */
bool nsockbuf_empty(nsock_iod nsi);

/* Is this event fake? Sometimes you can receive event that looks
   like NSE_TYPE_READ but in fact it's modified NSE_TYPE_TIMER.
   This function allows you check if this event is real or faked. */
bool nsockbuf_fake_read(nsock_event nse);

/* Just free() all structures for this NSI. You'll loose buffered data. */
void nsockbuf_free(nsock_iod nsi);


/*****************************************************************************/
/* implementation of NSE related functions. 
   It's copied from Diman's non-buffered implementation */
static int l_nsockbuf_receive_line(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);

	if(udata->nsiod == NULL) {
		lua_pushboolean(l, false);
		lua_pushstring(l, "Trying to receive line through a closed socket\n");
		return 2;	
	}

	nsockbuf_readline(nsp, udata->nsiod, l_nsockbuf_receive_handler, udata->timeout, l);
	return lua_yield(l, 0);
}

static int l_nsockbuf_receive_bytes(lua_State* l) {
	l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1);
	int nbytes = (int) luaL_checknumber(l, 2);

	if(udata->nsiod == NULL) {
		lua_pushboolean(l, false);
		lua_pushstring(l, "Trying to receive bytes through a closed socket\n");
		return 2;	
	}

	nsockbuf_readbytes(nsp, udata->nsiod, l_nsockbuf_receive_handler, udata->timeout, l, nbytes);
	return lua_yield(l, 0);
}

void l_nsockbuf_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
	lua_State* l = (lua_State*) lua_state;
	char* rcvd_string;
	int rcvd_len = 0;
	char* hexified;

	if(l_nsock_checkstatus(l, nse) == NSOCK_WRAPPER_SUCCESS) {
		rcvd_string = nsockbuf_readbuf(nse, &rcvd_len);

		if(o.scripttrace) {
			hexified = l_nsock_hexify((const void*) rcvd_string, (size_t) rcvd_len);
			l_nsock_trace(nse_iod(nse), hexified, FROM);
			free(hexified);
		}

		lua_pushlstring(l, rcvd_string, rcvd_len);
		process_waiting2running((lua_State*) lua_state, 2);
	} else {
		process_waiting2running((lua_State*) lua_state, 2);
	}
}



/*****************************************************************************/
/* Nsockbuf code */


/***** NSOCKBUF internals  */
/* Every instance of this structure is linked with exacly one nsi. */
struct nsockbuf_priv{
	int bytes_requested;	// Have user requested bytes?
	int lines_requested;	// Have user requested line?

	char *buf;		// receive buffer.
	int   buf_len;		// how many bytes are inside this buffer
	int   buf_allocated;	// how many bytes we allocated?
	char *buf_free;		// This is buffer that should be freed soon. It's given to user.

	// user's data
	nsock_pool nsp;
	nsock_iod  nsi;
	void *userdata;
	nsock_ev_handler callback;
	int timeout_msecs;

	bool fake_read;		// Is current read faked?
};
/* This mapps NSI with NSBP. We need to search for NSI sometimes. */
std::map <const nsock_iod, struct nsockbuf_priv *> nsockbuf_map;
/***** /NSOCKBUF internals  */


/*****************************************************************************/
/* nsockbuf functions */

// Free buffers for this nsi, I don't need it any more.
void nsockbuf_free(nsock_iod nsi){
	assert(nsi);
	struct nsockbuf_priv *nsbp = nsockbuf_map[nsi];
	if(!nsbp)
		return;
		
	if(o.debugging >= 3){
		if(nsbp->bytes_requested!=0 || nsbp->lines_requested!=0)
			error("WARNING: nsockbuf: freeing nsi that was waiting for data.");
		if(nsbp->buf_len)
			error("WARNING: nsockbuf: wasted %i bytes", nsbp->buf_len);
	}

	if(nsbp->buf_free)
		free(nsbp->buf_free);
	if(nsbp->buf)
		free(nsbp->buf);

	nsockbuf_map.erase(nsi);
	free(nsbp);
}

// Is internal buffer of 'nsockbuf' empty?
bool nsockbuf_empty(nsock_iod nsi){
	struct nsockbuf_priv *nsbp = NULL;
	if(nsi)
		nsbp = nsockbuf_map[nsi];
	if(nsbp && nsbp->buf_len)
		return(false);
	return(true);
}


// helper function for nsockbuf_readline
static struct nsockbuf_priv* nsockbuf_create_nsbp(nsock_pool nsp, nsock_iod nsi){
	struct nsockbuf_priv *nsbp;

	nsbp = (struct nsockbuf_priv *)safe_zalloc(sizeof(struct nsockbuf_priv));
	nsbp->nsp = nsp;
	nsbp->nsi = nsi;
	nsockbuf_map[nsi] = nsbp;
	return(nsbp);
}

// helper function for nsockbuf_readline
static void nsockbuf_fill(struct nsockbuf_priv *nsbp, nsock_ev_handler handler, int timeout_msecs, void *userdata){
	nsbp->callback = handler;
	nsbp->timeout_msecs = timeout_msecs;
	nsbp->userdata = userdata;
}

/* This handler is used to receive async nsock responses */
void nsockbuf_handler(nsock_pool nsp, nsock_event nse, void *);

// Request exacly one line.
nsock_event_id nsockbuf_readline(nsock_pool nsp, nsock_iod nsi,
                                 nsock_ev_handler handler, int timeout_msecs,
                                 void *userdata)
{
	nsock_event_id nse;
	struct nsockbuf_priv *nsbp = nsockbuf_map[nsi];
	if(!nsbp)	// create nsbp
		nsbp = nsockbuf_create_nsbp(nsp, nsi);
	assert(nsbp);

	if(nsbp->callback || nsbp->userdata || nsbp->bytes_requested || nsbp->lines_requested)
		fatal("nsockbuf: Can't handle two requests for the same connection %i %i",nsbp->bytes_requested, nsbp->lines_requested);

	nsbp->lines_requested = 1;
	nsockbuf_fill(nsbp, handler, timeout_msecs, userdata);

	if(nsbp->buf_len && memchr(nsbp->buf, '\n', nsbp->buf_len))	// already have valid data!
		nse = nsock_timer_create(nsbp->nsp, nsockbuf_handler, 0, nsbp);
	else
		nse = nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, timeout_msecs, nsbp);
	return(nse);
}

// Request exact number of bytes.
nsock_event_id nsockbuf_readbytes(nsock_pool nsp, nsock_iod nsi,
                                  nsock_ev_handler handler, int timeout_msecs,
                                  void *userdata, int nbytes)
{
	nsock_event_id nse;
	struct nsockbuf_priv *nsbp = nsockbuf_map[nsi];
	if(!nsbp)	// create nsbp
		nsbp = nsockbuf_create_nsbp(nsp, nsi);
	assert(nsbp);

	if(nsbp->callback || nsbp->userdata || nsbp->bytes_requested || nsbp->lines_requested)
		fatal("nsockbuf: Can't handle two requests for the same connection");

	if(nbytes <= 0)
		fatal("nsockbuf: you can't wait for 0 bytes");

	nsbp->bytes_requested = nbytes;
	nsockbuf_fill(nsbp, handler, timeout_msecs, userdata);

	if(nsbp->buf && (nsbp->buf_len >= nsbp->bytes_requested))	// already have data!
		nse = nsock_timer_create(nsbp->nsp, nsockbuf_handler, 0, nsbp);
	else
		nse = nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, timeout_msecs, nsbp);
	return(nse);
}


// Brutally change nsocks_event type and nsi.
// This is used to force change from TIMER to READ event.
void nsockbuf_event_change_type(nsock_event nse, enum nse_type type, nsock_iod nsi);

/* handles nsocks async responses */
void nsockbuf_handler(nsock_pool nsp, nsock_event nse, void *ud){
	enum nse_status status = nse_status(nse);
	enum nse_type type = nse_type(nse);
	struct nsockbuf_priv *nsbp = (struct nsockbuf_priv *) ud;
	assert(nsbp);
	assert(nsbp->callback);
	assert(nsbp->bytes_requested || nsbp->lines_requested);


	if(status == NSE_STATUS_SUCCESS &&
	   (type == NSE_TYPE_READ ||
	    type == NSE_TYPE_TIMER) )
	{
		if(type == NSE_TYPE_READ){
			int rec_len;
			char *rec = nse_readbuf(nse, &rec_len);
			// copy data to buffer. TODO: optimize this!!!!
			if(rec_len>0){
				if(!nsbp->buf){ 
					nsbp->buf = (char*)safe_malloc(rec_len);
					nsbp->buf_allocated = rec_len;
				}else if((nsbp->buf_allocated - (nsbp->buf_len+rec_len)) < 0){// not enough space
					nsbp->buf = (char*)safe_realloc(nsbp->buf, nsbp->buf_len + rec_len);
					nsbp->buf_allocated = nsbp->buf_len + rec_len;
				}
				memcpy(&nsbp->buf[nsbp->buf_len], rec, rec_len);
				nsbp->buf_len += rec_len;
			}
			assert(nsbp->buf_allocated >= nsbp->buf_len);
			// data okay? do callback
			if((nsbp->bytes_requested &&
			    nsbp->buf_len>=nsbp->bytes_requested)  ||
			   (nsbp->lines_requested &&
			    memchr(nsbp->buf, '\n', nsbp->buf_len)))
				nsbp->callback(nsp, nse, nsbp->userdata); // inform user that data has arrived
			else //data not okay? request more
				nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, nsbp->timeout_msecs, nsbp);
			return;
		}else{// TIMER
			// Ugly hack :) Pretend this was READ event, not TIMER
			nsockbuf_event_change_type(nse, NSE_TYPE_READ, nsbp->nsi);
			nsbp->fake_read = 1;
			nsbp->callback(nsp, nse, nsbp->userdata);
			nsbp->fake_read = 0;
			return;
		}
	}else // forward 'bad' events to user
		nsbp->callback(nsp, nse, nsbp->userdata);
}

char *nsockbuf_getbytes(struct nsockbuf_priv *nsbp, int* rec_len);
char *nsockbuf_getlines(struct nsockbuf_priv *nsbp, int* rec_len);

// Receive what you requested.
char *nsockbuf_readbuf(nsock_event nse, int *nbytes){
	nsock_iod nsi = nse_iod(nse);

	assert(nsi);

	struct nsockbuf_priv *nsbp;
	nsbp = nsockbuf_map[nsi];
	assert(nsbp);
	assert(nbytes);

	if(nsbp->bytes_requested)
		return(nsockbuf_getbytes(nsbp, nbytes));
	else if(nsbp->lines_requested)
		return(nsockbuf_getlines(nsbp, nbytes));
	else 
		fatal("nsockbuf: you haven't requested anything from this socket!");
	return(NULL);
}


// helper for nsockbuf_readbuf
char *nsockbuf_getbytes(struct nsockbuf_priv *nsbp, int* rec_len){
	assert(nsbp->bytes_requested);

	if(nsbp->buf_free){
		free(nsbp->buf_free);
		nsbp->buf_free = NULL;
	}
	if(nsbp->buf && nsbp->buf_len>=nsbp->bytes_requested){
		int out_len = nsbp->bytes_requested;
		char *out = (char*)safe_malloc(out_len+1);
		memcpy(out, nsbp->buf, out_len);
		out[out_len]='\0'; // it's much nicer to get real null-terminated string

		memmove(nsbp->buf, &nsbp->buf[out_len], nsbp->buf_len - out_len);
		nsbp->buf_len -= out_len;

		nsbp->buf_free = out;
		*rec_len = out_len;

		nsbp->bytes_requested = 0;
		nsbp->userdata = NULL;
		nsbp->callback = NULL;
		return(out);
	}

	// nsockbuf_getbytes called but no data availble
	*rec_len = 0;
	return("");
}

// helper for nsockbuf_readbuf
char *nsockbuf_getlines(struct nsockbuf_priv *nsbp, int* rec_len){
	assert(nsbp->lines_requested);

	if(nsbp->buf_free){
		free(nsbp->buf_free);
		nsbp->buf_free = NULL;
	}
	char *p;
	if( nsbp->buf && (p=(char*)memchr(nsbp->buf, '\n', nsbp->buf_len)) ){
		int out_len = (p - nsbp->buf)+1; // after '\n'
		char *out = (char*)safe_malloc(out_len+1);
		memcpy(out, nsbp->buf, out_len);
		out[out_len]='\0'; // it's much nicer to get real null-terminated string

		memmove(nsbp->buf, &nsbp->buf[out_len], nsbp->buf_len - out_len);
		nsbp->buf_len -= out_len;

		nsbp->buf_free = out;
		*rec_len = out_len;

		nsbp->lines_requested = 0;
		nsbp->userdata = NULL;
		nsbp->callback = NULL;
		return(out);
	}
	// nsockbuf_getlines called but no data availble
	*rec_len = 0;
	return("");
}

// Your handler can receive real event or just faked. You should distinguish them
// when you update your timers :)
bool nsockbuf_fake_read(nsock_event nse){
	if(!nse)
		return(false);

	nsock_iod nsi = nse_iod(nse);
	if(!nsi)
		return(false);

	struct nsockbuf_priv *nsbp;
	nsbp = nsockbuf_map[nsi];
	if(!nsbp)
		return(false);

	return(nsbp->fake_read);
}



// Brutally change nsocks_event type and nsi.
// This is used to force change from TIMER to READ event.
#include "nsock/src/nsock_internal.h"
void nsockbuf_event_change_type(nsock_event nse, enum nse_type type, nsock_iod nsi){
	struct msevent *mse = (struct msevent*)nse;
	mse->type = type;
	mse->iod  = (msiod*) nsi;
}




#endif /* NOLUA */
