/*
 * ioabs_tcp.c:
 * I/O abstraction layer for TCP.
 *
 * Copyright (c) 2002 Chris Lightfoot. All rights reserved.
 * Email: chris@ex-parrot.com; WWW: http://www.ex-parrot.com/~chris/
 *
 */

static const char rcsid[] = "$Id: ioabs_tcp.c,v 1.11 2003/01/09 23:20:23 chris Exp $";

#include <sys/types.h>

#include <errno.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>

#include <sys/select.h>

#include "connection.h"
#include "util.h"

/* ioabs_tcp_shutdown:
 * Shut down the socket connection. */
static int ioabs_tcp_shutdown(connection c) {
    shutdown(c->s, 2);
    close(c->s);
    c->cstate = closed;
    c->s = -1;
    return 1;   /* assume this succeeded. */
}

/* ioabs_tcp_immediate_write:
 * Write using write(2). */
static ssize_t ioabs_tcp_immediate_write(connection c, const void *buf, size_t count) {
    ssize_t n;
    struct ioabs_tcp *io;
    io = (struct ioabs_tcp*)c->io;
    if (c->cstate == closed)
        return IOABS_ERROR;
    do
        n = write(c->s, buf, count);
    while (n == -1 && errno == EINTR);
    if (n > 0) {
        c->nwr += n;
        c->idlesince = time(NULL);
    }
    if (n == -1) {
        if (errno == EAGAIN)
            return IOABS_WOULDBLOCK;
        else {
            log_print(LOG_ERR, _("ioabs_tcp_immediate_write: client %s: write: %m; closing connection"), c->idstr);
            ioabs_tcp_shutdown(c);
            return IOABS_ERROR;
        }
    } else
        return n;
}

/* ioabs_tcp_pre_select:
 * Simple pre-select handling for TCP. */
static void ioabs_tcp_pre_select(connection c, int *n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) {
    struct ioabs_tcp *io;
    io = (struct ioabs_tcp*)c->io;

    FD_SET(c->s, readfds);
    if (buffer_available(c->wrb) > 0)
        FD_SET(c->s, writefds);
    
    if (c->s > *n)
        *n = c->s;
}

/* ioabs_tcp_post_select:
 * Simple post-select handling for TCP. */
static int ioabs_tcp_post_select(connection c, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) {
    int ret = 0;
    ssize_t n;
    struct ioabs_tcp *io;
    io = (struct ioabs_tcp*)c->io;

    if (FD_ISSET(c->s, readfds)) {
        /* Can read data. */
        do {
            char *r;
            size_t rlen;
            /* Ensure that we have lots of space to read.... */
            buffer_expand(c->rdb, MAX_POP3_LINE);
            r = buffer_get_push_ptr(c->rdb, &rlen);
            do
                n = read(c->s, r, rlen);
            while (n == -1 && errno == EINTR);
            if (n > 0) {
                buffer_push_bytes(c->rdb, n);
                c->nrd += n;
                ret = 1;
            }
        } while (n > 0);
        if (n == 0) {
            /* Connection has been closed. */
            log_print(LOG_INFO, _("ioabs_tcp_post_select: client %s: connection closed by peer"), c->idstr);
            ioabs_tcp_shutdown(c);
            return 0;
        } else if (n == -1 && errno != EAGAIN) {
            log_print(LOG_ERR, _("ioabs_tcp_post_select: client %s: read: %m; closing connection"), c->idstr);
            ioabs_tcp_shutdown(c);
            return 0;
        }
    }

    if (FD_ISSET(c->s, writefds) && buffer_available(c->wrb) > 0) {
        /* Can write data. */
        n = 1;
        do {
            char *w;
            size_t wlen;
            if (!(w = buffer_get_consume_ptr(c->wrb, &wlen)))
                break; /* no more data to write */
            do
                n = write(c->s, w, wlen);
            while (n == -1 && errno == EINTR);
            if (n > 0) {
                buffer_consume_bytes(c->wrb, n);
                c->nwr += n;
                c->idlesince = time(NULL);
            }
        } while (n > 0);
        if (n == -1 && errno != EAGAIN) {
            log_print(LOG_ERR, _("ioabs_tcp_post_select: client %s: write: %m; closing connection"), c->idstr);
            ioabs_tcp_shutdown(c);
        }
    }

    return ret;
}

/* ioabs_tcp_destroy:
 * The only resource to be destroyed is the memory allocated for the
 * structure. */
static void ioabs_tcp_destroy(connection c) {
    xfree(c->io);
}

/* ioabs_tcp_create:
 * Create a struct ioabs_tcp. */
struct ioabs_tcp *ioabs_tcp_create(void) {
    struct ioabs_tcp *io;
    io = xmalloc(sizeof *io);
    io->und.immediate_write = ioabs_tcp_immediate_write;
    io->und.pre_select      = ioabs_tcp_pre_select;
    io->und.post_select     = ioabs_tcp_post_select;
    io->und.shutdown        = ioabs_tcp_shutdown;
    io->und.destroy         = ioabs_tcp_destroy;
    return io;
}


syntax highlighted by Code2HTML, v. 0.9.1