/*
 * © 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 "netbase.h"
#include <setjmp.h>
#include <signal.h>
#include <time.h>
#include "ecaccess.h"
#include "mars.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, NULL);
        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, (caddr_t)&net->soc,
                      (xdrproc)&readtcp, (xdrproc)&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;
        socklen_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, (caddr_t)&net->soc,
                      (xdrproc)&readtcp, (xdrproc)&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, NULL);
                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, NULL);
        }

        net->cb_soc = -1;

        xdrrec_create(&net->x, net->bufsize, net->bufsize, (caddr_t)&net->soc,
                      (xdrproc)&readtcp, (xdrproc)&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);


    /* 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.so_sndbuf, mars.so_rcvbuf, (caddr_t)&net.soc,
                  (xdrproc)&readtcp, (xdrproc)&writetcp);


    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
