/*
 * Copyright (c) 2004-2009, Luiz Otavio O Souza <loos.br@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

static const char rcsid[] = "$Id: client.c 112 2009-03-15 17:30:28Z loos-br $";

#include <sys/types.h>
#include <sys/socket.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "client.h"
#include "return.h"
#include "msn-proxy.h"

void
client_sched_read(client_ *client) {
    if (EVENT_FD((&client->read)) == -1) return;
    event_add(&client->read, &config.timeout_client_read);
}

void
client_sched_write(client_ *client) {
    if (EVENT_FD((&client->write)) == -1) return;
    event_add(&client->write, &config.timeout_client_write);
}

client_ *
client_alloc(  int client_fd,
			void (*fn_read)(int, short, void *),
			void (*fn_write)(int, short, void *),
			void *arg) {
 client_		*client = (client_ *)0;

    client = (client_ *)malloc(sizeof(*client));
    if (client == (client_ *)0)
	die_nomem();
    memset(client, 0, sizeof(*client));

    /* prepare commands */
    commands_init(&client->commands);

    /* set fd */
    client->fd = client_fd;

    /* set events */
    event_set(&client->read, client->fd, EV_READ, fn_read, arg);
    event_set(&client->write, client->fd, EV_WRITE, fn_write, arg);

    /* sched read */
    client_sched_read(client);

    return(client);
}

int
client_flush_commands(client_ *client) {
 command                *cmd = client->commands.cmd;
 command                *next;

    /* clean commands */
    while (cmd) {
	next = cmd->next;
	free_command(cmd);
	cmd = next;
    }

    /* reset pointers */
    client->commands.cmd      = (command *)0;
    client->commands.cmd_last = (command *)0;

    /* clean rx buffer */
    str_free(&client->commands.buf);

    return(0);
}

client_ *
client_disconnect(client_ *client) {
    /* is client connected ? */
    if (client == (client_ *)0)
	return((client_ *)0);

    /* disconnect */
    real_client_disconnect(client);

    /* flush commands */
    client_flush_commands(client);

    /* release client */
    free(client);
    return((client_ *)0);
}

void
real_client_disconnect(client_ *client) {

    /* delete i/o events */
    if (EVENT_FD((&client->read)) != -1) {
        event_del(&client->read);
	client->read.ev_fd = -1;
    }
    if (EVENT_FD((&client->write)) != -1) {
        event_del(&client->write);
	client->write.ev_fd = -1;
    }

    /* close connection */
    if (client->fd != -1) {
        while (close(client->fd) != 0 && errno == EINTR);
	client->fd = -1;
    }
}

void
shift_client_commands(client_ *client, command *cmd) {
    client->commands.cmd = cmd->next;
    if (client->commands.cmd == (command *)0) {
	client->commands.cmd_last = (command *)0;
    }
}

