/****************************************************************************
 * Copyright (C) 1998 WIDE Project. All rights reserved.
 * Copyright (C) 1999,2000,2001,2002 University of Tromso. All rights reserved.
 * Copyright (C) 2002 Invenia Innovation AS. All rights reserved.
 *
 * Author: Feike W. Dillema, feico@pasta.cs.uit.no.
 *         based on newbie code by Yusuke DOI, Keio Univ. Murai Lab.
 ****************************************************************************/

/*
 * <$Id: ev_tcp.c,v 3.7 2002/03/04 12:34:10 dillema Exp $>
 */

#include "totd.h"

/* TCP event lists */
static G_List *ConnL_head;
static G_List *ETOL_head;

/*
 * TCP service handling
 */

int ev_tcp_srv_accept (int srv_sock) {
	struct sockaddr_storage peer;
	unsigned int peer_len;
	int conn_sock;

	if (T.debug > 2)
		syslog (LOG_DEBUG, "ev_tcp_srv_accept(): sock = %d", srv_sock);

	peer_len = sizeof (struct sockaddr_storage);
	conn_sock = accept (srv_sock, (struct sockaddr *)&peer, &peer_len);
	if (conn_sock < 0) {
		syslog (LOG_ERR, "accept: %m");
		return -1;
	}

	return tcp_response_start (conn_sock, (struct sockaddr *)&peer);
}

/*
 * Generic TCP event handling routines
 */

int ev_tcp_common_eq (void *arg1, void *arg2) {
	Ev_TCP_Common_Data *etcd_arg1;
	Ev_TCP_Common_Data *etcd_arg2;

	etcd_arg1 = arg1;
	etcd_arg2 = arg2;

	if (etcd_arg1->sock == etcd_arg2->sock)
		return 0;
	else
		return 1;
}

G_List *ev_tcp_common_init (void) {
	G_List *gl;

	gl = list_init ();
	if (!gl) 
		return NULL;

	gl->list_data = NULL;
	return gl;
}

int ev_tcp_common_register (G_List *gl, int sock, Context *cptr) {
        const char *fn = "ev_tcp_common_register()";
	Ev_TCP_Common_Data *etcd_new;

	if (T.debug > 2)
		syslog (LOG_DEBUG, "%s: sock = %d", fn, sock);

	etcd_new = malloc (sizeof (Ev_TCP_Common_Data));
	if (!etcd_new)
		return -1;

	etcd_new->sock = sock;
	etcd_new->cptr = cptr;

	if (list_add (gl, etcd_new) < 0)
		return -1;

	return 0;
}

int ev_tcp_common_remove (G_List *gl, int sock) {
        const char *fn = "ev_tcp_common_remove()";
	Ev_TCP_Common_Data etcd_tmp;
	Ev_TCP_Common_Data *etcd_p;
	G_List *gl_tmp;

	if (T.debug > 2)
		syslog (LOG_DEBUG, "%s: sock = %d", fn, sock);

	etcd_tmp.sock = sock;
	etcd_tmp.cptr = NULL;

	gl_tmp = list_search (gl, (void *) &etcd_tmp, ev_tcp_common_eq);
	if (!gl_tmp)
		return -1;

	etcd_p = (Ev_TCP_Common_Data *) list_delete (gl_tmp);
	free (etcd_p);

	return 0;
}

int ev_tcp_common_fds (G_List *gl, fd_set *fds) {
	Ev_TCP_Common_Data *etcd_p;
	G_List *gl_tmp;
	int maxid = 0;

	gl->list_data = NULL;
	for (gl_tmp = gl->next; gl_tmp->list_data; gl_tmp = gl_tmp->next) {
		etcd_p = ((Ev_TCP_Common_Data *) (gl_tmp->list_data));
		if (T.debug > 2)
			syslog (LOG_DEBUG, "ev_tcp_common_fds: FD_SET(%d)",
				etcd_p->sock);
		FD_SET (etcd_p->sock, fds);
		maxid = MAXNUM (etcd_p->sock, maxid);
	}

	return maxid;
}

int ev_tcp_common_fd_check (G_List *gl, fd_set *fds) {
	G_List *gl_tmp;
	Context *cont;
	Ev_TCP_Common_Data *etcd_p;
	int s_tmp;

	for (gl_tmp = gl->next; gl_tmp->list_data; gl_tmp = gl_tmp->next) {
		etcd_p = (Ev_TCP_Common_Data *) (gl_tmp->list_data);
		s_tmp = etcd_p->sock;

		if (FD_ISSET (s_tmp, fds)) {
			FD_CLR (s_tmp, fds);

			/*
			 * etcd_p MAY be released within context, so keep
			 * context alone
			 */
			cont = etcd_p->cptr;

			/* resume context */
			return cont->process (cont);
		}
	}
	return 0;
}

/*
 * TCP incoming connection event list
 */

int ev_tcp_conn_in_init (void) {
	if (ConnL_head)
		return -1;

	ConnL_head = ev_tcp_common_init ();
	if (!ConnL_head)
		return -1;
	return 0;
}

void ev_tcp_conn_in_finish (void) {
	list_destroy (ConnL_head, free);
}

int ev_tcp_conn_in_register (int sock, Context *cptr) {
	if (T.debug > 3)
		syslog (LOG_DEBUG, "ev_tcp_conn_in_register(): start");
	return ev_tcp_common_register (ConnL_head, sock, cptr);
}

int ev_tcp_conn_in_remove (int sock) {
	if (T.debug > 3)
		syslog (LOG_DEBUG, "ev_tcp_conn_in_remove(): start");
	return ev_tcp_common_remove (ConnL_head, sock);
}

int ev_tcp_conn_in_fds (fd_set *fds) {
	if (T.debug > 3)
		syslog (LOG_DEBUG, "ev_tcp_conn_in_fds(): start");
	return ev_tcp_common_fds (ConnL_head, fds);
}

int ev_tcp_conn_in_fd_check (fd_set *fds) {
	if (T.debug > 3)
		syslog (LOG_DEBUG, "ev_tcp_conn_in_fd_check(): start");
	return ev_tcp_common_fd_check (ConnL_head, fds);
}

/*
 * TCP outgoing connection event list
 */

int ev_tcp_out_init (void) {

	if (ETOL_head)
		return -1;

	ETOL_head = ev_tcp_common_init ();
	if (!ETOL_head)
		return -1;

	return 0;
}

void ev_tcp_out_finish (void) {
	list_destroy (ETOL_head, free);
}

int ev_tcp_out_register (int sock, Context * cptr) {
	if (T.debug > 3)
		syslog (LOG_DEBUG, "ev_tcp_out_register(): start");
	return ev_tcp_common_register (ETOL_head, sock, cptr);
}

int ev_tcp_out_remove (int sock) {
	if (T.debug > 3)
		syslog (LOG_DEBUG, "ev_tcp_out_remove(): start");
	return ev_tcp_common_remove (ETOL_head, sock);
}

int ev_tcp_out_fds (fd_set * fds) {
	if (T.debug > 3)
		syslog (LOG_DEBUG, "ev_tcp_out_fds(): start");
	return ev_tcp_common_fds (ETOL_head, fds);
}

int ev_tcp_out_fd_check (fd_set * fds) {
	if (T.debug > 3)
		syslog (LOG_DEBUG, "ev_tcp_out_fd_check(): start");
	return ev_tcp_common_fd_check (ETOL_head, fds);
}



syntax highlighted by Code2HTML, v. 0.9.1