/*
 * © Copyright 1996-2012 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include "mars.h"
#include "ecaccess.h"
#include "base.h"
#include "netbase.h"
#include <signal.h>
#include <setjmp.h>
#include <time.h>

#if mars_client_HAVE_RPC

#ifdef sgi
#include <bstring.h>
#endif

#ifdef CRAY
typedef u_long xdr_t;
#else
typedef void *xdr_t;
#endif

static jmp_buf env;

#define NET_BUFFER	20480

typedef struct netdata {

    boolean server;
	int    port;
	char   *host;
	int    soc;
	int    cb_soc;
	XDR    x;
	netblk blk;
	request *r;
	request *e;
	int     retries;
	boolean compress;
	boolean validate_grib;
	boolean callback;
	int     timeout;
	int     bufsize;
	pid_t   pid;
	time_t  time;
	char    *validate;

	boolean    ecaccess;
	char      *echost;
	char      *ecaccesshome;
	int        eccertport;
	char      *eccommand;
	char      *ecservice;
} netdata;


static void netbase_init(void);

static err     netbase_open(void *data,request*,request*,int);
static err     netbase_close(void *data);
static err     netbase_read(void *data,request *r,void *buffer,long *length);
static err     netbase_write(void *data,request *r,void *buffer,long *length);
static err     netbase_cntl(void *data,int code,void *param,int size);
static boolean netbase_check(void *data,request *r);
static err     netbase_validate(void *data,request*,request*,int);

static option opts[] = {
	{"port","NETBASE_PORT",NULL,"7000",t_int,
	sizeof(int),OFFSET(netdata,port)},
	{"host","NETBASE_HOST",NULL,"munin",t_str,
	sizeof(char*),OFFSET(netdata,host)},
	{"retry","NETBASE_RETRY","-retry","5",t_int,
	sizeof(int),OFFSET(netdata,retries)},
	{"compress",NULL,NULL,"0",t_boolean,
	sizeof(boolean),OFFSET(netdata,compress)},
	{"validate_grib","VALIDATE_GRIB",NULL,"0",t_boolean,
	sizeof(boolean),OFFSET(netdata,validate_grib)},
	{"timeout","NETBASE_TIMEOUT","-timeout","0",t_int,
	sizeof(int),OFFSET(netdata,timeout)},
	{"callback","NETBASE_CALLBACK",NULL,"0",t_boolean,
	sizeof(boolean),OFFSET(netdata,callback)},
	{"bufsize",NULL,NULL,"65536",t_int,
	sizeof(int),OFFSET(netdata,bufsize)},
	{"validate","NETBASE_VALIDATE",NULL,NULL,t_str,
	sizeof(char*),OFFSET(netdata,validate)},

	/* ecaccess stuff */
	{"ecaccess",NULL,NULL,"0",t_boolean,
	sizeof(boolean),OFFSET(netdata,ecaccess)},
	{"echost","ECHOST",NULL,"ecaccess.ecmwf.int",t_str,
	sizeof(char*),OFFSET(netdata,echost)},
    {"ecaccesshome","ECACCESS_HOME",NULL,".",t_str,
    sizeof(char*),OFFSET(netdata,ecaccesshome)},
    {"eccertport","ECCERTPORT",NULL,"443",t_int,
    sizeof(int),OFFSET(netdata,eccertport)},
    {"eccommand","ECCOMMAND",NULL,"client/tools/eccert",t_str,
    sizeof(char*),OFFSET(netdata,eccommand)},
    {"ecservice","EC_MARS_SERVICE",NULL,"ecmars",t_str,
    sizeof(char*),OFFSET(netdata,ecservice)},
};

static base_class _netbase_base = {

	NULL,                          /* parent class */
	"netbase",                     /* name         */

	false,                         /* inited       */

	sizeof(netdata),               /* private_size  */
	NUMBER(opts),                  /* options_count */
	opts,                          /* options       */

	netbase_init,                  /* init          */

	netbase_open,                  /* open          */
	netbase_close,                 /* close         */

	netbase_read,                  /* read          */
	netbase_write,                 /* write         */

	netbase_cntl,                  /* control       */

	netbase_check,                 /* check         */

	NULL,                      	   /* query        */

	NULL,                          /* archive      */
	NULL,                          /* admin        */

	netbase_validate,              /* validate */

};

static long compression_encode(unsigned char* in, long in_len, unsigned char* out, long out_len)
{
    marslog(LOG_EROR,"Compression not supported");
    return -1;
}

static long compression_decode(unsigned char* in, long in_len, unsigned char* out, long out_len)
{
    marslog(LOG_EROR,"Compression not supported");
    return -1;
}

/* the only 'public' variable ... */

base_class *netbase = &_netbase_base;

static void netbase_init(void)
{
}

static void catch_alarm(int sig)
{
	longjmp(env,1);
}

static request *messages = NULL;

static void msgout(int lvl,const char *msg)
{

	lvl &= ~(LOG_ONCE);

	if(messages == NULL)
		messages = empty_request("MESSAGES");

	add_value(messages,"LVL","%d",lvl);
	add_value(messages,"MSG","%s",msg);
}

static void putmsgs(netdata *net)
{
	request *r = net->blk.env;
	if(r)
	{
		int n = 0;
		const char *lvl,*msg;
		while( (lvl = get_value(r,"LVL",n)) && (msg = get_value(r,"MSG",n)))
		{
			marslog(atoi(lvl),"%s [%s]",msg,database_name(net));
			n++;
		}
	}
}

static void freeblk(netdata *net)
{
	err e = net->blk.error;

	xdr_free((xdrproc_t) xdr_netblk, (char*) &net->blk);
	bzero(&(net->blk),sizeof(netblk));
	net->blk.error = e;
}

static err sendblk(netdata *net)
{
	if(net->callback && net->server)
	{
		static request *r = 0;
		net->callback = false;
		net->soc = call_server(net->host,net->port,20);
		if(net->soc == -1) {
			marslog(LOG_EROR,"Failed to reconnect to client %s %d",
				net->host,net->port);
			return -2;
		}
		xdrrec_create(&net->x,net->bufsize,net->bufsize,(mxdrparam)&net->soc,
			(mxdrproc)readtcp,(mxdrproc)writetcp);

		
		if(!r) r = empty_request("check");
		set_value(r,"cb_pid","%ld",(long)net->pid);
		set_value(r,"cb_time","%ld",(long)net->time);
		net->blk.env = r;
	}

	if(setjmp(env) != 0) {
		marslog(LOG_WARN,"Timeout when sending data to %s",net->host);
		close(net->soc);net->soc = -1; 
		return 1;
	}

	if(net->timeout) {
		signal(SIGALRM,catch_alarm);
		alarm(net->timeout*60);
	}

	net->x.x_op = XDR_ENCODE;

	marslog(LOG_DBUG,"sendblk");

	if(!xdr_netblk(&net->x,&net->blk))
	{
		marslog(LOG_WARN,"Failed to send data to %s",net->host);
		if(net->timeout) alarm(0);
		return 1;
	}

	if(!xdrrec_endofrecord(&net->x,true))
	{
		marslog(LOG_WARN,"Failed to send data to %s",net->host);
		if(net->timeout) alarm(0);
		return 1;
	}

	if(net->timeout) alarm(0);


	if(net->callback && !net->server) 
	{
		close(net->soc); 
		net->soc = -1;
		xdr_destroy(&net->x);
	}

	return 0;
}

static err recvblk(netdata *net)
{
	int check;

retry:
	check = 0;

	if(net->callback && !net->server)
	{
		struct sockaddr_in from;
		marssocklen_t fromlen = sizeof(from);

		net->callback = false;
		net->soc    = accept(net->cb_soc,(struct sockaddr*)&from, &fromlen);
		if(net->soc <0)
		{
			 marslog(LOG_WARN|LOG_PERR,"accept");
			 return -2;
		}

		xdrrec_create(&net->x,net->bufsize,net->bufsize,(mxdrparam)&net->soc,
			(mxdrproc)readtcp,(mxdrproc)writetcp);

		check = 1;
	}

	if(setjmp(env) != 0) {
		marslog(LOG_WARN,"Timeout when receiving data from %s",net->host);
		close(net->soc);net->soc = -1; 
		return 1;
	}

	if(net->timeout) {
		signal(SIGALRM,catch_alarm);
	    alarm(net->timeout*60);
	}

	net->x.x_op = XDR_DECODE;

	marslog(LOG_DBUG,"recvblk");

	if(!xdrrec_skiprecord(&net->x))
	{
		marslog(LOG_WARN,"Failed to receive data from %s",net->host);
		if(net->timeout) alarm(0);
		return 1;
	}

	if(!xdr_netblk(&net->x,&net->blk))
	{
		marslog(LOG_WARN,"Failed to receive data from %s",net->host);
		if(net->timeout) alarm(0);
		return 1;
	}

	if(net->timeout) alarm(0);

	if(check)
	{
		const char *p = get_value(net->blk.env,"cb_pid",0);
		const char *t = get_value(net->blk.env,"cb_time",0);

		if(p == 0 || t == 0 || atol(p) != net->pid || atol(t) != net->time)
		{
			marslog(LOG_WARN,"Wrong callback !!");
			net->callback = true;
			close(net->soc);net->soc = -1;
			xdr_destroy(&net->x);
			goto retry;
		}
	}

	return 0;
}


static err ecaccess_connect(netdata *net, char *host, int *port, char *uid)
{
	int perr = 0;
    char buf[BUFSIZ];
	char cmd[10240];
    FILE *file;
	char *p,*q;

	marslog(LOG_DBUG,"ecaccess_connect new version"); 

    sprintf(cmd,"%s/%s -echost %s -ecport %d -tunnel %s",
			net->ecaccesshome,
			net->eccommand,
			net->echost,
			net->eccertport,
			net->ecservice
			);

	marslog(LOG_DBUG,"Command to execute: '%s'",cmd);

	if ((file = popen(cmd, "r")) == NULL)
	{
		marslog(LOG_EROR|LOG_PERR,"Cannot run %s",cmd);
		return -1;
	}

	memset(buf,0,sizeof(buf));
	fgets(buf, BUFSIZ - 1, file); 
	if(strlen(buf)) buf[strlen(buf)-1] = 0;

	if( (perr = pclose(file)) != 0)
	{
		marslog(LOG_EROR,"pclose(%s) returns %d.",cmd,perr);
		return -1;
	}

	/* parse the received string */
	p = buf;
	q = p;

	host[0] = uid[0] = 0; *port = 0;

	while(*p)
	{
		switch(*p)
		{
			case '@':
				*p = 0;
				strcpy(uid,q);
				q = p+1;
				*p = '@';
				break;

			case ':':
				*p  = 0;
				strcpy(host,q);
				*port = atoi(p+1);
				*p = ':';
				break;
		}
		p++;
	}

	if(!host[0] || !uid[0] || !*port)
	{
		marslog(LOG_EROR,"Cannot parse [%s] output of %s",buf,cmd);
		return -1;
	}

    return 0;	
}


static err  netbase_open(void *data,request *r,request *v,int mode)
{
	netdata *net = (netdata*)data;
	err     e = NOERR;
	int     code;
	int     wait;

	for(;;)
	{

		signal(SIGPIPE,SIG_IGN);

		if(net->ecaccess)
		{
			char host[1024];
			char uid[80];
			int  port;

			/* update the ecaccess host & port */ 
			if(ecaccess_connect(net,host,&port,uid) != NOERR)
			{
				marslog(LOG_EROR,"Received error %d",e);
				net->soc = -1;
			}
			else
			{
				marslog(LOG_INFO,"Connecting to ecaccess host %s port %d as user %s",host,port,uid);
				net->soc    = call_server(host,port,net->retries);
				marslog(LOG_INFO,"Connected to ecaccess host %s port %d as user %s",host,port,uid);
				set_value(v,"user","%s",uid);
			}
		}
		else
		{
			net->soc    = call_server(net->host,net->port,net->retries);
		}

		net->cb_soc = -1;

		xdrrec_create(&net->x,net->bufsize,net->bufsize,(mxdrparam)&net->soc,
			(mxdrproc)readtcp,(mxdrproc)writetcp);

		marslog(LOG_DBUG,"netbase_open");

		if(net->soc<0)
			return -2;

		bzero(&net->blk,sizeof(netblk));
		net->blk.code = NET_OPEN;
		net->blk.req  = r;
		net->blk.env  = v;
		net->blk.mode = mode;
		net->blk.data.data_len = mars.certlen;
		net->blk.data.data_val = mars.certstr;

		if(mars.infomissing)
			net->blk.mode |= NET_MISSING;

		if(net->callback)
		{
			int port;
			char host[80];

			net->cb_soc = server_mode(&port,host);

			if(net->cb_soc == -1)
			{
				marslog(LOG_EROR,"Callback mode switched off");
				net->callback = false;
			}
			else
			{
				net->pid  = getpid();
				net->time = time(0);

				set_value(v,"cb_port","%d",port);
				set_value(v,"cb_host","%s",host);
				set_value(v,"cb_pid","%ld",(long)net->pid);
				set_value(v,"cb_time","%ld",(long)net->time);

				net->blk.mode |= NET_CALLBACK;
			}
		}

		if(sendblk(net)) e = -2;

		net->blk.req  = NULL;
		net->blk.env  = NULL;
		net->blk.data.data_len = 0;
		net->blk.data.data_val = 0;

		if(e == 0)
			if(recvblk(net))
				e = -2;

		wait = net->blk.mode;
		code = net->blk.code;

		putmsgs(net);
		freeblk(net);

		e = e?e:net->blk.error;

		if(e || (code != NET_WAIT))
			return e;

		/* for the time being, don't wait */

		return -1;
		/*NOTREACHED*/

		/* wait a little bit ... */

		close(net->soc);
		xdr_destroy(&net->x);

		marslog(LOG_INFO,"Server on %s port %d is busy, waiting...",
		    net->host,net->port);

		if(wait < 1  ) wait = 1;
		if(wait > 60 ) wait = 60;
		sleep(wait);

	}

}

static err  netbase_close(void *data)
{
	netdata *net = (netdata*)data;
	err        e = 0;
	marslog(LOG_DBUG,"netbase_close");

	if(net->soc != -1)
	{
		bzero(&net->blk,sizeof(netblk));
		net->blk.code = NET_CLOSE;

		if(sendblk(net)) e = -2;
		else
			if(recvblk(net)) e = -2;

		marslog(LOG_DBUG,"netbase_close");
		putmsgs(net);

		e =  e?e:net->blk.error;
		freeblk(net);

		close(net->soc);
	}
	xdr_destroy(&net->x);

	return e;
}

static boolean is_valid(netdata *net,request *r,void *buffer,long length)
{
	boolean b;
	if(!net->validate_grib) return true;
	if(observation(r)) return true;

#ifdef COMEBACK
	b = validate_grib(buffer,length);
#else
	b = true;
#endif

	if(!b) print_all_requests(r);

	return b;
}

static err timed_recvblk(netdata *net, timer *t)
{
	int e;
	long64 total = 0;
	timer_start(t);
	if((e = recvblk(net)) == NOERR)
		total = net->blk.bufsize;
	timer_stop(t,total);

	return e;
}

static err  netbase_read(void *data,request *r,void *buffer,long *length)
{
	netdata *net = (netdata*)data;
	char timermsg[1024];
	timer   *nettimer;

	sprintf(timermsg,"Transfer from %s",net->host);
	nettimer=get_timer(timermsg,"transfertime",true);

	marslog(LOG_DBUG,"netbase_read");

	bzero(&net->blk,sizeof(netblk));

	net->blk.code = NET_READ;
	net->blk.mode = net->compress?NET_COMPRESS:0; /* ask for compression */

	net->blk.bufsize = *length;

	if(sendblk(net)) {
		freeblk(net);
		return -2;
	}

	net->blk.data.data_val = buffer;

	if(timed_recvblk(net,nettimer)) {
		net->blk.data.data_val = NULL;
		putmsgs(net);
		freeblk(net);
		return -2;
	}

	putmsgs(net);
	/* was the data compressed ? */

	if(net->blk.error == 0 && net->blk.mode == NET_COMPRESS)
	{
		char *temp = malloc(*length);
		if(temp)
		{
			long len   = compression_decode((unsigned char*)buffer,
			    net->blk.data.data_len,
			    (unsigned char*)temp,*length);


			if(len < 0) {
				net->blk.error   = -3; /* too small */
				net->blk.bufsize = -len;
			} else {
				memcpy(buffer,temp,len);
				net->blk.bufsize = len;
			}
			FREE(temp);
		}
	}

	net->blk.data.data_val = NULL;

	if(r && net->blk.req) {
		if(mars.debug) {
			print_all_requests(r);
			print_all_requests(net->blk.req);
		}
		reqcpy(r,net->blk.req);
	}

	*length  =  net->blk.bufsize;

	freeblk(net);

	if((net->blk.error == 0) && !is_valid(net,r,buffer,*length))
		return -2;

	return net->blk.error;
}

static err  netbase_write(void *data,request *r,void *buffer,long *length)
{
	netdata *net = (netdata*)data;
	err e = 0;

	marslog(LOG_DBUG,"netbase_write");

	if(!is_valid(net,r,buffer,*length))
		return -2;

	bzero(&net->blk,sizeof(netblk));
	net->blk.code = NET_WRITE;

	net->blk.data.data_len = *length;
	net->blk.data.data_val = buffer;
	net->blk.req           = r;

	if(sendblk(net)) e = -2;

	net->blk.req           = NULL;
	net->blk.data.data_val = NULL;
	net->blk.data.data_len = 0;

	if(e == 0)
	{
		if(recvblk(net)) e = -2;
		*length  =  net->blk.bufsize;
	}

	putmsgs(net);
	freeblk(net);

	return e?e:net->blk.error;

}

static err netbase_list(void *data, request *r)
{
	char buffer[1024];
	long length = sizeof(buffer);
	int n;
	FILE *f = stdout;
	const char *target = no_quotes(get_value(r,"TARGET",0));
	err e = 0;

	if(target)
	{
		f  = fopen(target,target_open_mode(target));
		if(!f) 
		{
			marslog(LOG_EROR|LOG_PERR,"fopen(%s)",target);
			return -2;
		}
	}

	while( (n = netbase_read(data,r,buffer,&length))  == NOERR )
	{
		fwrite(buffer,1,length,f);
		length = sizeof(buffer);
	}

	if(n != EOF)
		marslog(LOG_WARN,"Got error %d during list",n);

	if(ferror(f))
	{
		marslog(LOG_EROR|LOG_PERR,"Error during list");
		e = -2;
	}

	if(target && f) 
	{
		if(fclose(f) != 0)
		{
			marslog(LOG_EROR|LOG_PERR,"fclose(%s)",target);
			return -2;
		}
	}

	return e;
}

static err  netbase_cntl(void *data,int code,void *param,int size)
{
	switch(code)
	{
	case CNTL_LIST:
		netbase_list(data,(request*)param);
		return 0;
		/*NOTREACHED*/
		break;

	default:
		return -1;
		/*NOTREACHED*/
		break;
	}
	return -1;
}

static boolean  netbase_check(void *data,request *r)
{
	netdata *net = (netdata*)data;
	boolean b;
	err     e = NOERR;

	marslog(LOG_DBUG,"netbase_check");

	bzero(&net->blk,sizeof(netblk));
	net->blk.code = NET_CHECK;

	net->blk.req           = r;
	if(sendblk(net)) e  = -2;
	net->blk.req           = NULL;

	if(e == 0) recvblk(net);
	putmsgs(net);

	if(net->blk.error || e)
		b = false;
	else
		b = net->blk.check;


	freeblk(net);

	return b;

}
/*====================================================================*/
/* Server side.                                                       */
/*====================================================================*/



static database *net_open(netdata *net,base_class *driver)
{
	database *b;

	if(net->blk.mode & NET_CALLBACK)
	{
		net->callback = true;
		net->pid      = atol(get_value(net->blk.env,"cb_pid",0));
		net->time     = atol(get_value(net->blk.env,"cb_time",0));
		net->port     = atoi(get_value(net->blk.env,"cb_port",0));
		net->port     = atoi(get_value(net->blk.env,"cb_port",0));
		net->host     = strcache(get_value(net->blk.env,"cb_host",0));
	}

	if(net->blk.mode & NET_MISSING)
		mars.infomissing = true;

	net->r = clone_all_requests(net->blk.req);
	net->e = clone_all_requests(net->blk.env);



#ifndef METVIEW
#ifdef ECMWF

	if(mars.certify)
	{
		int e;

		set_environ(net->e);

		 e = certify(net->r,net->e,
			net->blk.data.data_val,
			net->blk.data.data_len);
			
		if(e){
			net->blk.error = -2;
			freeblk(net);
			return NULL;
		}
	}
	else
	{
		const char *user = get_value(net->e,"user",0);
		new_user(user);
		print_all_requests(get_environ());
		print_all_requests(net->r);
	}
#endif
#endif

	b = database_open(driver,NULL,net->r,net->e,net->blk.mode&0x7);

	net->blk.req = NULL;
	net->blk.env = NULL;

	if(b == NULL)
		net->blk.error = -2;

	freeblk(net);

	return b;
}

static err net_close(netdata *net,database *b)
{
	free_all_requests(net->r);
	free_all_requests(net->e);
	freeblk(net);
	return net->blk.error = database_close(b);
}


static err net_read(netdata *net,database *b)
{
	long length  = net->blk.bufsize;
	char *buffer = MALLOC(length);
	request   *r = empty_request(NULL);
	boolean comp = net->blk.mode == NET_COMPRESS;

	freeblk(net);


	net->blk.error = database_read(b,r,buffer,&length);
	net->blk.bufsize = length;

	if((net->blk.error == 0) && !is_valid(net,r,buffer,length))
		net->blk.error = -2;

	if(comp && net->blk.error == 0) /* compression requested */
	{
		char *temp = (char*)MALLOC(length);
		long len   = compression_encode((unsigned char*)buffer,length,
		    (unsigned char*)temp,length);

		if(len < 0)
		{
			/* compression failed */
			FREE(temp);
		}
		else
		{
			/* compression succeded */
			net->blk.mode = NET_COMPRESS;
			FREE(buffer);
			buffer = temp;
			length = len;
		}
	}

	if(net->blk.error == NOERR)
	{
		net->blk.data.data_len = length;
		net->blk.data.data_val = buffer;
	}

	net->blk.req  = r;

	marslog(LOG_DBUG,"net_read : %d",length);

	return net->blk.error;
}

static err net_write(netdata *net,database *b)
{
	long length  = net->blk.data.data_len;
	char *buffer = net->blk.data.data_val;
	request *r   = clone_all_requests(net->blk.req);

	if(is_valid(net,r,buffer,length))
		net->blk.error = database_write(b,r,buffer,&length);
	else
		net->blk.error = -2;


	free_all_requests(r);
	/* net->blk.req = NULL; */

	freeblk(net);

	net->blk.bufsize = length;

	return net->blk.error;
}

static err net_check(netdata *net,database *b)
{
	boolean c =  database_check(b,net->blk.req);
	freeblk(net);
	net->blk.check = c;
	return NOERR;
}

static err net_raw(netdata *net,database *b)
{
	return 0;
}

void basetask(int soc,int count, void *data)
{

	base_class *driver = (base_class*)data;
	netdata  net;
	database *b = NULL;
	boolean  stop = false;

	bzero(&net,sizeof(net));

	net.soc    = soc;
	net.server = true;

	signal(SIGPIPE,SIG_IGN);

#if 0
	xdrrec_create(&net.x,net.bufsize,net.bufsize,(mxdrparam)&net.soc,
		(mxdrproc)readtcp,(mxdrproc)writetcp);
#else
	/* Improve transfers:
		- Set socket buffer sizes to MARS_SOCKBUF
		- Set buffer sizes to the same as socket buffer sizes
	*/
	socket_buffers(net.soc);
	xdrrec_create(&net.x,mars.sockbuf,mars.sockbuf,(mxdrparam)&net.soc,
		(mxdrproc)readtcp,(mxdrproc)writetcp);

#endif

	marslog(LOG_DBUG,"Starting basetask %d...",count);
	net.host = strcache(host_of(addr_of(soc)));

	mars.outproc = msgout;


	while(!stop)
	{

		marslog(LOG_DBUG,"Waiting...");
		/* dumpmem(); */

		if(recvblk(&net))
			stop = true;

		switch(net.blk.code)
		{

		case NET_OPEN:
			/* Tell remote guy to sleep a bit ... */
			if(mars.maxforks && count >= mars.maxforks)
			{
				bzero(&(net.blk),sizeof(netblk));
				net.blk.code = NET_WAIT;
				net.blk.mode = 20;      /* 20 sec. */
				stop = true;
			}
			else
			{
				marslog(LOG_DBUG,"get NET_OPEN");
				b = net_open(&net,driver);
				if(b == NULL)
					stop = true;
			}
			break;

		case NET_CLOSE:
			marslog(LOG_DBUG,"get NET_CLOSE");
			net_close(&net,b);
			stop = true;
			b = NULL;
			break;

		case NET_READ:
			marslog(LOG_DBUG,"get NET_READ");
			if(net_read(&net,b) != 0)
				/* stop = true; */
				;
			break;

		case NET_WRITE:
			marslog(LOG_DBUG,"get NET_WRITE");
			if(net_write(&net,b) != 0)
				/* stop = true; */
				;
			break;

		case NET_CHECK:
			marslog(LOG_DBUG,"get NET_CHECK");
			if(net_check(&net,b) != 0)
				/* stop = true; */
				;
			break;

		case NET_ABORT:
			marslog(LOG_INFO,"Abort cmd received");
			marsexit(9);
			break;

		case NET_RAW:
			net_raw(&net,b);
			break;

		default:
			if(b) net_close(&net,b);
			marslog(LOG_EROR,"Unknown tag received : %d",net.blk.code);
			stop = true;
			break;

		}

		net.blk.env = messages;

		if(sendblk(&net))
			stop = true;

		free_all_requests(messages);
		net.blk.env = messages = NULL;

		freeblk(&net);

	}

	mars.outproc = NULL;

	database_admin(driver);

	marslog(LOG_DBUG,"Ending basetask...");

}

static err netbase_validate(void *data, request *r, request *e, int mode)
{
	netdata *net = (netdata*)data;
	err ret = NOERR;
	
	if(net->validate)
		ret = validate_request(r,e,net->validate);

	return ret;
}

#endif
