/* ARISA - Interface and DCC Send/Recv/Chat Functions
 * Copyright (C) 2003, 2004 Carl Ritson
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#define WANT_POLL
#define WANT_STATFS

#include "arisa.h"
#include "user.h"

#include "sendfile.h"
#include "line-drive.h"

#include "dcc.h"

#include <math.h>

#include <arpa/inet.h>

#include <sys/time.h>
#include <sys/stat.h>

#include <unistd.h>
#include <fcntl.h>
#include <utime.h>

#include <errno.h>

/** Defines and Macros **/

#define POLL_ERRORS (POLLERR | POLLHUP | POLLNVAL)

/** Typedefs and Enums **/

typedef enum cl_colour_t {
	CL_GREEN	= 0x01,
	CL_YELLOW	= 0x02,
	CL_RED		= 0x04,
	CL_WHITE	= 0x08
} cl_colour_t;

typedef enum functype_t {
	CHECK_PREPOLL	= 0,
	CHECK_POSTPOLL	= 1,
	CONN_PREPOLL	= 2,
	CONN_POSTPOLL	= 3,
	NEGO_PREPOLL	= 4,
	NEGO_POSTPOLL	= 5,
	DELETE		= 6,
	RUN_PREPOLL	= 7,
	RUN_POSTPOLL	= 8,
	RUN		= 9,
	BANDWIDTH	= 10,
	FUNC_MAX	= 11
} functype_t;

typedef enum call_t { 
	DOWNWARD	= 0, 
	UPWARD		= 1, 
	INPLACE		= 2 
} call_t;

typedef enum ns_t {
	NS_NOCHANGE	= 0,
	NS_CONNECT	= 1, 
	NS_NEGOTIATE	= 2, 
	NS_RUN		= 3, 
	NS_DELETE	= 4
} ns_t;

typedef struct cl_t	cl_t;
typedef struct node_t	node_t;
typedef struct sok_t	sok_t;
typedef struct intf_t	intf_t;

typedef int (*func_t)(call_t, functype_t, intf_t *, node_t *);
#define FUNC_TYPE(funcname) \
	static int funcname(call_t ct, functype_t ft, intf_t *intf, node_t *n)


/** Struct Definitions **/

struct cl_t {
	cl_colour_t	colour;
	node_t 		*front;
	int 		size;
};

struct node_t {
	ns_t		state;
	cl_colour_t	colour;
	
	uint32_t	p_rate;	/* present rate, for this time slice */
	uint32_t	c_rate; /* ceiling rate */
	uint32_t	a_rate; /* assured rate */
	uint32_t	max_rate; /* externally configured maximum speed */
	uint32_t	min_rate; /* externally configured minimum speed */
	
	node_t	*next;
	node_t	*prev;
	
	/* sub nodes, in coloured lists */
	cl_t	*green;
	cl_t	*yellow;
	cl_t	*red;
	cl_t	*white;
	
	func_t	funcs[FUNC_MAX];
	int	last_ret;
	void	*data;

	/* mostly shared */
	int	fd;
	int	sok;
	int	err;
	
	off_t	ack;
	off_t	pos;
	off_t	size;
	
	short	events;
	struct	pollfd	*poll;

	char	*file;
	int	complete;
	
	/* send - specific */
	pool_t	*pool;
	
	/* recv - specific */
	time_t 	connected; // only for DCC SERVER

	/* chat - specific */
	time_t	time_limit;

	linebuf_t *snd_buf;
	linebuf_t *rcv_buf;
};

struct sok_t {
	int		port;
	int		sok;
	int		inuse;
	struct pollfd 	*poll;
};

struct intf_t {
	interface_t	*interface;
	interfacetype_t	type;
	int		mode;
	int		end;
	
	int		ticks_low;
	int		ticks_high;
	
	unsigned short	port;
	unsigned short	no_ports;
	unsigned short	dccserver_port;
	
	unsigned int	dcc_timeout;
	int		accept_per_sec;
	int		secure_mode;
	off_t		max_size;
	
	size_t		token_size;
	size_t		snd_bufsize;
	size_t		rcv_bufsize;
	
	char		*bind_ip;
	struct	in_addr	p_bind_ip;
	char		*advertise_ip;
	struct	in_addr	p_advertise_ip;

	struct	pollfd	*fds;
	int		no_fds;
	int		size_fds;
	
	sok_t		**sok_used;
	pqueue_t	*sok_unused;
	int		no_soks;
	int		no_bound;
	
	char		*buffer;
	size_t		bufsize;

	node_t		*root;
};


/** allocators and deallocators **/

static cl_t *alloc_cl(cl_colour_t colour) {
	cl_t *c = xalloc(sizeof(cl_t));
	c->colour	= colour;
	c->front 	= NULL;
	c->size		= 0;
	return c;
}

static void free_cl(cl_t *c) {
	xfree(c);
}


/** circular list manipulation functions **/

static inline int CL_ISEMPTY(cl_t *c) {
	if(c->size == 0)
		return 1;
	else
		return 0;
}

static inline node_t *CL_FRONT(cl_t *c) {
	return c->front;
}

static inline node_t *CL_BACK(cl_t *c) {
	if(c->front != NULL)
		return c->front->prev;
	else
		return NULL;
}

static inline void CL_FOREACH(cl_t *c, void (*func)(node_t *)) {
	node_t *p, *n = c->front;
	if(c->size == 0)
		return;
	do {
		p = n;
		n = n->next;
		func(p);
	} while(n != c->front);
}

static inline node_t *CL_POP(cl_t *c, node_t *n) {
	if(c->size == 0 || n == NULL)
		return NULL;
	n->prev->next = n->next;
	n->next->prev = n->prev;
	if(n == c->front) {
		if(c->size == 1)
			c->front = NULL;
		else
			c->front = n->next;
	}
	c->size--;
	return n;
}
#define	CL_POP_BACK(c) 		CL_POP((c),CL_BACK((c)))
#define CL_POP_FRONT(c) 	CL_POP((c),CL_FRONT((c)))

static inline void CL_PUSH(cl_t *c, node_t *n, int front) {
	if(n == NULL)
		return;
	if(c->size == 0) {
		c->front = n;
		n->next = n;
		n->prev = n;
	} else {
		n->next = c->front;
		n->prev = c->front->prev;
		c->front->prev->next = n;
		c->front->prev = n;
		if(front)
			c->front = n;
	}
	n->colour = c->colour;
	c->size++;
}
#define	CL_PUSH_FRONT(c,n)	CL_PUSH((c),(n),1)
#define CL_PUSH_BACK(c,n)	CL_PUSH((c),(n),0)

static int CL_IN_LIST(cl_t *c, node_t *node) {
	node_t *n = c->front;
	if(n != NULL) {
		do {
			if(n == node)
				return 1;
		} while(n != c->front);
	}
	return 0;
}

static int CL_DATA_IN_LIST(cl_t *c, void *data) {
	node_t *n = c->front;
	if(n != NULL) {
		do {
			if(n->data == data)
				return 1;
			n = n->next;
		} while(n != c->front);
	}
	return 0;
}

static inline node_t *CL_REMOVE_NODE(cl_t *c, node_t *n) {
	if(c->size > 1) {
		n->prev->next = n->next;
		n->next->prev = n->prev;
		if(c->front == n)
			c->front = n->next;
	} else
		c->front = NULL;
	c->size--;
	return n;
}


/** Rest of allocators and deallocators **/

static node_t *alloc_node(void) {
	node_t *n = xalloc(sizeof(node_t));
	int i;
	
	n->state	= NS_DELETE; // FIXME: is this a good idea?
	n->colour	= CL_GREEN;
	
	n->p_rate 	= 0;
	n->c_rate 	= 0;
	n->a_rate 	= 0;
	n->max_rate	= 0;
	n->min_rate	= 0;
	
	n->next		= n;
	n->prev		= n;
	
	n->green 	= alloc_cl(CL_GREEN);
	n->yellow 	= alloc_cl(CL_YELLOW);
	n->red 		= alloc_cl(CL_RED);
	n->white 	= alloc_cl(CL_WHITE);
	
	for(i = 0; i < FUNC_MAX; ++i)
		n->funcs[i] = NULL;
	
	n->last_ret	= 0;
	n->data		= NULL;
	
	n->fd		= -1;
	n->sok		= -1;
	n->err		= 0;
	
	n->ack		= 0;
	n->pos		= 0;
	n->size		= 0;
	n->err		= 0;

	n->events	= 0;
	n->poll		= NULL;

	n->file		= NULL;
	n->complete	= 0;

	n->pool		= NULL;
	
	n->connected	= 0;

	n->time_limit	= 0;
	
	n->snd_buf	= NULL;
	n->rcv_buf	= NULL;
	
	return n;
}

static void free_node(intf_t *intf, node_t *n); // pre-declaration
static void free_cl_and_nodes(intf_t *intf, cl_t *cl) {
	int i;
	if(cl == NULL)
		return;
	for(i = cl->size; i > 0; --i) {
		node_t *n = CL_POP_FRONT(cl);
		free_node(intf,n);
	}
	free_cl(cl);
}

static int run_func(call_t ct, functype_t ft, intf_t *intf, node_t *n); // pre-declaration
static void close_socket(intf_t *intf, int *sokp); // pre-declaration
static void close_fd(int *fdp); // pre-declaration
static void free_node(intf_t *intf, node_t *n) {
	//D("intf: %p, n: %p",intf,n);
	
	run_func(INPLACE,DELETE,intf,n);
	free_cl_and_nodes(intf,n->green);
	free_cl_and_nodes(intf,n->yellow);
	free_cl_and_nodes(intf,n->red);
	free_cl_and_nodes(intf,n->white);
	if(n->fd != -1)
		close_fd(&(n->fd));
	if(n->sok != -1)
		close_socket(intf,&(n->sok));
	if(n->file != NULL)
		xfree(n->file);
	if(n->snd_buf != NULL)
		free_linebuf(n->snd_buf);
	if(n->rcv_buf != NULL)
		free_linebuf(n->rcv_buf);
	xfree(n);
}

static intf_t *alloc_intf(interfacetype_t type, interface_t *interface) {
	intf_t *intf = xalloc(sizeof(intf_t));
	
	intf->interface 	= interface;
	intf->type		= type;
	intf->mode		= 0;
	intf->end		= 0;
	
	intf->ticks_low		= 4;
	intf->ticks_high	= 8;
	
	intf->port		= 0;
	intf->no_ports		= 0;
	intf->dccserver_port	= 0;
	
	intf->dcc_timeout	= 180;
	intf->accept_per_sec	= 1;
	intf->secure_mode	= 0;
	intf->max_size		= 0;
	
	intf->token_size	= 1024;
	intf->snd_bufsize 	= 0;
	intf->rcv_bufsize	= 0;
	
	intf->bind_ip		= NULL;
	intf->advertise_ip	= NULL;
	
	intf->fds		= xalloc(sizeof(struct pollfd)*4);
	intf->no_fds		= 0;
	intf->size_fds		= 4;
	
	intf->sok_used		= NULL;
	intf->sok_unused	= NULL;
	intf->no_soks		= 0;
	intf->no_bound		= 0;

	intf->bufsize		= 4096;
	intf->buffer 		= xalloc(intf->bufsize);

	intf->root		= alloc_node();
	intf->root->data	= interface;
	
	return intf;
}

static void close_sok(int *sok); // pre-declaration
static void free_intf(intf_t *intf) {
	if(intf->root != NULL)
		free_node(intf,intf->root);
	if(intf->bind_ip != NULL)
		xfree(intf->bind_ip);
	if(intf->advertise_ip != NULL)
		xfree(intf->advertise_ip);
	if(intf->fds != NULL)
		xfree(intf->fds);
	if(intf->sok_used != NULL) {
		int i;
		for(i = 0; i < intf->no_soks; ++i) {
			if(intf->sok_used[i] != NULL) {
				if(intf->sok_used[i]->sok != -1)
					close_sok(&(intf->sok_used[i]->sok));
				xfree(intf->sok_used[i]);
			}
		}
		xfree(intf->sok_used);
	}
	if(intf->sok_unused != NULL) {
		sok_t *s;
		while((s = pqueue_pop_front(intf->sok_unused)) != NULL) {
			if(s->sok != -1)
				close_sok(&(s->sok));
			xfree(s);
		}
		pqueue_deinit(intf->sok_unused,xfree,0);
	}
	if(intf->buffer != NULL)
		xfree(intf->buffer);
	xfree(intf);
}


/** Node Function Calling */

static int run_func(call_t ct, functype_t ft, intf_t *intf, node_t *n); // pre-declaration
static void run_cl_func(call_t ct, functype_t ft, intf_t *intf, cl_t *cl) {
	node_t *n;
	int i,ret;
	
	if(cl == NULL)
		return;
	
	for(i = cl->size; i > 0; --i) {
		n = CL_POP_FRONT(cl);
		ret = run_func(ct,ft,intf,n);
		if(ret != NS_DELETE) {
			int tmp = n->colour;
			CL_PUSH_BACK(cl,n);
			n->colour = tmp;
		} else
			free_node(intf,n);
	}
}

static int make_call(call_t ct, functype_t ft, intf_t *intf, node_t *n) {
	func_t f = NULL;
	int ret;
	
	//D("%d %d %p %p",ct,ft,intf,n);
	
	switch (ft) {
		case BANDWIDTH:
		case CHECK_PREPOLL:
		case CHECK_POSTPOLL:
		case DELETE:
			f = n->funcs[ft];
			break;
		case CONN_PREPOLL:
		case CONN_POSTPOLL:
			if(n->state == NS_CONNECT)
				f = n->funcs[ft];
			break;
		case NEGO_PREPOLL:
		case NEGO_POSTPOLL:
			if(n->state == NS_NEGOTIATE)
				f = n->funcs[ft];
			break;
		case RUN_PREPOLL:
		case RUN_POSTPOLL:
		case RUN:
			if(n->state == NS_RUN)
				f = n->funcs[ft];
			break;
		default:
			assert(0);
			break;
	}
	if(f != NULL) {
		ret = f(ct,ft,intf,n);
		if(ret != NS_NOCHANGE)
			n->state = ret;
		return ret;
	} else
		return NS_NOCHANGE;
}

static int run_func(call_t ct, functype_t ft, intf_t *intf, node_t *n) {
	int ret = NS_NOCHANGE;
	
	//D("%d %d %p %p",ct,ft,intf,n);
	
	if(intf == NULL || n == NULL)
		return NS_NOCHANGE;

	if(ct == INPLACE || ct == DOWNWARD) {
		ret = make_call(ct,ft,intf,n);
		if(ret == NS_DELETE)
			return ret;
	}
	
	if(ct != INPLACE) {
		run_cl_func(ct,ft,intf,n->green);
		run_cl_func(ct,ft,intf,n->yellow);
		run_cl_func(ct,ft,intf,n->red);
		run_cl_func(ct,ft,intf,n->white);
	}

	if(ct == UPWARD)
		ret = make_call(ct,ft,intf,n);

	return ret;
}

static inline int N_RUN(intf_t *intf, node_t *n) {
	make_call(INPLACE,RUN,intf,n);
	return n->last_ret;
}

static inline void set_func(node_t *node, functype_t type, func_t func) {
	node->funcs[type] = func;
}


/** Logging Functions **/

static char *hostport_str(struct sockaddr_in *addr, char *buffer, size_t bufsize) {
	char addrbuf[64];
	inet_ntop(AF_INET,&(addr->sin_addr),addrbuf,sizeof(addrbuf));
	snprintf(buffer,bufsize,"%s:%u",addrbuf,ntohs(addr->sin_port));
	return buffer;
}

static char *peer_hostport_str(int sok, char *buffer, size_t bufsize) {
	struct sockaddr_in addr;
	socklen_t len = sizeof(addr);
	getpeername(sok,(struct sockaddr *)&addr,&len);
	return hostport_str(&addr,buffer,bufsize);
}

static char *sock_hostport_str(int sok, char *buffer, size_t bufsize) {
	struct sockaddr_in addr;
	socklen_t len = sizeof(addr);
	getsockname(sok,(struct sockaddr *)&addr,&len);
	return hostport_str(&addr,buffer,bufsize);
}

static char *netnick_str(intf_t *intf, node_t *n, char *buffer, size_t bufsize) {
	char netname[64],nick[64];
	network_t *net = NULL;

	netname[0] = '\0';
	nick[0] = '\0';
	
	if(intf->type == INTERFACE_SEND || intf->type == INTERFACE_RECV) {
		send_t *s = (send_t *)n->data;
		LOCK(s);
		if(s->nick != NULL)
			xstrncpy(nick,s->nick,sizeof(nick));
		net = s->network;
		UNLOCK(s);
	} else if(intf->type == INTERFACE_CHAT) {
		chat_t *c = (chat_t *)n->data;
		LOCK(c);
		if(c->nick != NULL)
			xstrncpy(nick,c->nick,sizeof(nick));
		net = c->network;
		UNLOCK(c);
	}

	if(net != NULL) {
		LOCK(net);
		xstrncpy(netname,net->name,sizeof(netname));
		UNLOCK(net);
	}

	snprintf(buffer,bufsize,"%s%s%s",
			netname, netname[0] != '\0' ? ":" : "", nick);
	return buffer;
}

static char *file_str(intf_t *intf, node_t *n, char *buffer, size_t bufsize) {
	if(intf->type == INTERFACE_SEND || intf->type == INTERFACE_RECV) {
		send_t *s = (send_t *)n->data;
		xstrncpy(buffer,"FILE",bufsize);
		LOCK(s);
		if(s->pack != NULL) {
			RLOCK(s->pack);
			if(s->pack->file != NULL)
				xstrncpy(buffer,s->pack->file,bufsize);
			RUNLOCK(s->pack);
		}
		UNLOCK(s);
	} else if(intf->type == INTERFACE_CHAT) {
		xstrncpy(buffer,"CHAT",bufsize);
	} else {
		xstrncpy(buffer,"UNKNOWN",bufsize);
	}
	return buffer;
}

static void log_unverifiable(intf_t *intf, 
		node_t *n, struct sockaddr_in *addr) { 
	char hp[64],nn[128],fs[256];
	LOGTP(L_INT,"Unable to verify that connection from (%s) as [%s] for {\"%s\"}, closing.",
			hostport_str(addr,hp,sizeof(hp)),
			netnick_str(intf,n,nn,sizeof(nn)),
			file_str(intf,n,fs,sizeof(fs)));
}

static void log_verified(intf_t *intf, 
		node_t *n, struct sockaddr_in *addr) {
	char hp[64],nn[128];
	LOGTP(L_INT,"Verified connection from (%s) for [%s].",
			hostport_str(addr,hp,sizeof(hp)),
			netnick_str(intf,n,nn,sizeof(nn)));
}

static void log_unverified(intf_t *intf, 
		node_t *n, struct sockaddr_in *addr) {
	char hp[64],nn[128],fs[256];
	LOGTP(L_INT,"Accepted unverified connection from (%s) as [%s] for {\"%s\"}.",
			hostport_str(addr,hp,sizeof(hp)),
			netnick_str(intf,n,nn,sizeof(nn)),
			file_str(intf,n,fs,sizeof(fs)));
			
}

static void log_connect_attempt(intf_t *intf, 
		node_t *n, struct sockaddr_in *addr) { 
	char hp[64],nn[128],fs[256];
	LOGTP(L_INT,"Attempting to connect to (%s) [%s] for {\"%s\"}.",
			hostport_str(addr,hp,sizeof(hp)),
			netnick_str(intf,n,nn,sizeof(nn)),
			file_str(intf,n,fs,sizeof(fs)));
}

static void log_upload_active(intf_t *intf, node_t *n) {
	char hs[64],ps[64];
	LOGTP(L_INT,"Connection Established (%s) <-> (%s) for upload of {\"%s\",%lld}.",
			sock_hostport_str(n->sok,hs,sizeof(hs)),
			peer_hostport_str(n->sok,ps,sizeof(ps)),
			n->file,n->pos);
}

static void log_dccserv_upload(intf_t *intf, node_t *n) {
	char ps[64];
	LOGTP(L_INT,"Connection from (%s) to DCC Server port negotiated as upload.",
			peer_hostport_str(n->sok,ps,sizeof(ps)));
}

static void log_dccserv_close(intf_t *intf, node_t *n) {
	char ps[64],errbuf[96];
	if(n->err == 0) {
		LOGTP(L_INT,"Closing failed connection attempt from (%s) to DCC Server port.",
				peer_hostport_str(n->sok,ps,sizeof(ps)));
	} else {
		LOGTP(L_INT,"Closing failed connection attempt from (%s) to DCC Server port, Error: %s.",
				peer_hostport_str(n->sok,ps,sizeof(ps)),
				lstrerror_r(n->err,errbuf,sizeof(errbuf)));
	}
}

static void log_dccserv_incoming(intf_t *intf, node_t *n) {
	char ps[64];
	LOGTP(L_INT,"Received connection from (%s) to DCC Server port.",
			peer_hostport_str(n->sok,ps,sizeof(ps)));
}

static void log_connected(intf_t *intf, node_t *n) {
	char hs[64],ps[64],nn[128];
	LOGTP(L_INT,"Connection Established (%s) <-> (%s) as [%s] for {\"%s\",%lld}.",
			sock_hostport_str(n->sok,hs,sizeof(hs)),
			peer_hostport_str(n->sok,ps,sizeof(ps)),
			netnick_str(intf,n,nn,sizeof(nn)),
			n->file != NULL ? n->file : "CHAT",n->pos);
}


/** Socket Functions **/

static struct pollfd *poll_add(intf_t *intf, int sok, int events) {
	int no = intf->no_fds;
	
	//D("intf: %p, sok: %d, events: %x",intf,sok,events);
	
	if(intf->no_fds == intf->size_fds) {
		intf->size_fds += 4;
		intf->fds = xreloc(intf->fds,
				intf->size_fds * sizeof(struct pollfd));
	}

	intf->no_fds++;
	intf->fds[no].fd	= sok;
	intf->fds[no].events	= events;
	intf->fds[no].revents	= 0;
	
	return &(intf->fds[no]);
}

static void poll_wipe(intf_t *intf) {
	intf->no_fds = 0;
}

static int set_nonblocking(int sok) {
	return fcntl(sok,F_SETFL,O_NONBLOCK);
}

static int alloc_socket(struct sockaddr_in *addr) {
	char errbuf[96], addrbuf[64];
	int sok,ret;

	sok = socket(PF_INET, SOCK_STREAM, 0);
	if(sok == -1) {
		E(errno);
		LOGTP(L_ERR,"Failed to allocate socket: %s",
				lstrerror_r(errno,errbuf,sizeof(errbuf)));
		return -1;
	}
	
#ifdef SO_REUSEADDR
	{
		int tmp = 1;
		setsockopt(sok,SOL_SOCKET,SO_REUSEADDR,&tmp,sizeof(tmp));
	}
#endif
	
	if(addr != NULL) {
		ret = bind(sok,(struct sockaddr *)addr,
				sizeof(struct sockaddr_in));
		if(ret == -1) {
			E(errno);
			LOGTP(L_ERR,"Failed to bind socket to (%s): %s",
				hostport_str(addr,addrbuf,sizeof(addrbuf)),
				lstrerror_r(errno,errbuf,sizeof(errbuf)));
			close_sok(&sok);
			return -1;
		}
	}

	ret = set_nonblocking(sok);
	if(ret == -1) {
		E(errno);
		LOGTP(L_ERR,"Failed to set socket (%s) non-blocking: %s",
			sock_hostport_str(sok,addrbuf,sizeof(addrbuf)),
			lstrerror_r(errno,errbuf,sizeof(errbuf)));
		close_sok(&sok);
		return -1;
	}

	return sok;
}

static int get_socket(intf_t *intf) {
	struct sockaddr_in addr;
	
	addr.sin_family = AF_INET;
	addr.sin_port = htons(0);
	
	if(intf->bind_ip != NULL) {
		memcpy(&(addr.sin_addr),&(intf->p_bind_ip),
				sizeof(struct in_addr));
	} else {
		addr.sin_addr.s_addr = INADDR_ANY;
	}

	return alloc_socket(&addr);
}

static int get_listening_socket(intf_t *intf) {
	if(intf->port != 0) {
		sok_t *sok = NULL;
		int i;
	
		sok = pqueue_pop_front(intf->sok_unused);
		
		if(sok == NULL) {
			errno = EAGAIN;
			return -1;
		}

		//D("intf->no_soks = %d",intf->no_soks);
		for(i = 0; i < intf->no_soks; ++i) {
			//D("intf->sok_used[%d] = %p",
			//	i,intf->sok_used[i]);
			if(intf->sok_used[i] == NULL) {
				intf->sok_used[i] = sok;
				break;
			}
		}

		sok->inuse = 1;
		return sok->sok;
	} else {
		int ret, sok = get_socket(intf);
		if(sok != -1) {
			ret = listen(sok,1);
			if(ret == -1) {
				char addrbuf[64],errbuf[96];
				E(errno);
				LOGTP(L_ERR,"Failed to listen on (%s): %s",
					sock_hostport_str(sok,addrbuf,
						sizeof(addrbuf)),
					lstrerror_r(errno,errbuf,
						sizeof(errbuf)));
				close_sok(&sok);
				return -1;
			}
		}
		return sok;
	}
}

static void close_sok(int *sok); // pre-declaration
static void close_socket(intf_t *intf, int *sokp) {
	if(intf->port == 0 || intf->no_soks == 0) {
		close_sok(sokp);
	} else {
		sok_t *sok = NULL;
		int i;

		for(i = 0; i < intf->no_soks && sok == NULL; ++i) {
			if(intf->sok_used[i] != NULL) {
				//D("intf->sok_used[%d]->sok = %u, *sokp = %u",
				//	i,intf->sok_used[i]->sok,*sokp);
				if(intf->sok_used[i]->sok == *sokp) {
					sok = intf->sok_used[i];
					intf->sok_used[i] = NULL;
				}
			}
		}
		if(sok != NULL) {
			sok->inuse = 0;
			pqueue_push_back(intf->sok_unused,sok);
		} else {
			close_sok(sokp);
		}
	}
	*sokp = -1;
}

static int accept_socket(int sok, struct sockaddr *addr, socklen_t *len) {
	int ret, asok;

	asok = accept(sok,addr,len);
	if(asok == -1)
		return -1;
	
	ret = set_nonblocking(asok);
	if(ret == -1) {
		char addrbuf[64],errbuf[96];
		E(errno);
		LOGTP(L_ERR,"Failed to set socket (%s) non-blocking: %s",
			sock_hostport_str(asok,addrbuf,sizeof(addrbuf)),
			lstrerror_r(errno,errbuf,sizeof(errbuf)));
		close_sok(&asok);
		return -1;
	}
	
	return asok;
}

static int socket_error(int sok) {
	socklen_t len;
	int ret, val;
	len = sizeof(val);
	ret = getsockopt(sok,SOL_SOCKET,SO_ERROR,&val,&len);
	return ret != -1 ? val : errno;
}

static int set_sndbuf(intf_t *intf, int sok) {
	int size = (int) intf->snd_bufsize;
	return setsockopt(sok,SOL_SOCKET,SO_SNDBUF,&size,sizeof(int));
}

static int set_rcvbuf(intf_t *intf, int sok) {
	int size = (int) intf->rcv_bufsize;
	return setsockopt(sok,SOL_SOCKET,SO_RCVBUF,&size,sizeof(int));
}

static void close_sok(int *sok) {
	shutdown(*sok,SHUT_RDWR);
	close(*sok);
	//D("close: %d",*sok);
	*sok = -1;
}

static void close_fd(int *fd) {
	close(*fd);
	//D("close: %d",*fd);
	*fd = -1;
}

static int do_connect(intf_t *intf, node_t *n, struct sockaddr_in *addr) {
	int ret;
	
	n->sok = get_socket(intf);
	if(n->sok == -1 && errno == EAGAIN)
		return 0;
	else if(n->sok == -1) {
		n->err = errno;
		E(n->err);
		return -1;
	}
		
	log_connect_attempt(intf,n,addr);
	ret = connect(n->sok,(struct sockaddr *)addr,
			sizeof(struct sockaddr_in));
	if(ret == -1 && errno != EINPROGRESS) {
		n->err = errno;
		E(n->err);
		return -1;
	}
	return 1;
}


/** Settings **/

static int set_bind_ip(intf_t *intf, const char *host) {
	struct in_addr *addrs;
	int no_addrs;
	
	intf->p_bind_ip.s_addr = INADDR_ANY;
	
	no_addrs = aresolv_lookup(global->aresolver,host,&addrs);
	if(no_addrs > 0) {
		if(intf->bind_ip != NULL)
			xfree(intf->bind_ip);
		intf->bind_ip = xstrdup(host);
		memcpy(&(intf->p_bind_ip),
				&(addrs[0]),sizeof(struct in_addr));
		xfree(addrs);
		return 0;
	} else
		return -1;
}

static int set_advertise_ip(intf_t *intf, const char *host) {
	struct in_addr *addrs;
	int no_addrs;

	intf->p_advertise_ip.s_addr = INADDR_ANY;
	
	no_addrs = aresolv_lookup(global->aresolver,host,&addrs);
	if(no_addrs > 0) {
		if(intf->advertise_ip != NULL)
			xfree(intf->advertise_ip);
		intf->advertise_ip = xstrdup(host);
		memcpy(&(intf->p_advertise_ip),
				&(addrs[0]),sizeof(struct in_addr));
		xfree(addrs);
		return 0;
	} else
		return -1;
}

static void intf_rehash_ports(intf_t *intf) {
	struct sockaddr_in addr;
	socklen_t len;
	int i,j,found,used;
	pqueue_t list, to_keep;
	sok_t *s;
	
	pqueue_init(&list,0);
	pqueue_init(&to_keep,0);
	
	if(intf->sok_used != NULL) {
		for(i = 0; i < intf->no_soks; ++i) {
			if(intf->sok_used[i] != NULL)
				pqueue_push_back(&list,intf->sok_used[i]);
		}
	}
	if(intf->sok_unused != NULL) {
		while((s = pqueue_pop_front(intf->sok_unused)) != NULL)
			pqueue_push_back(&list,s);
	}
	
	while((s = pqueue_pop_front(&list)) != NULL) {
		if(intf->mode == IMODE_NORMAL && 
				s->port >= intf->port && 
				(s->port < (intf->port + intf->no_ports))) {
			len = sizeof(addr);
			getsockname(s->sok,(struct sockaddr *)&addr,&len);
			if(addr.sin_addr.s_addr == INADDR_ANY &&
					intf->bind_ip == NULL) {
				pqueue_push_back(&to_keep,s);
				continue;
			} else if(memcmp(&(addr.sin_addr),
					&(intf->p_bind_ip),
					sizeof(struct in_addr)) == 0) {
				pqueue_push_back(&to_keep,s);
				continue;
			}
		}
		if(!s->inuse)
			close_sok(&(s->sok));
		xfree(s);
			
	}
	
	if(intf->sok_used != NULL &&
		(intf->no_ports == 0 || intf->mode != IMODE_NORMAL)) {
		xfree(intf->sok_used);
		intf->sok_used	= NULL;
	}
	if(intf->sok_unused != NULL && 
		(intf->no_ports == 0 || intf->mode != IMODE_NORMAL)) {
		pqueue_deinit(intf->sok_unused,xfree,0);
		intf->sok_unused = NULL;
	}
	
	if(intf->no_ports == 0 || intf->mode != IMODE_NORMAL) {
		intf->no_soks	= 0;
		intf->no_bound	= 0;
		return;
	}

	intf->no_bound = 0;
	intf->no_soks = intf->no_ports;
	intf->sok_used = xreloc(intf->sok_used,
			sizeof(sok_t *) * intf->no_soks);
	for(i = 0, used = -1; i < intf->no_soks; ++i) {
		intf->sok_used[i] = pqueue_pop_front(&to_keep);
		if(used == -1 && intf->sok_used[i] == NULL)
			used = i;
	}
	if(used == -1)
		used = intf->no_soks;
	if(intf->sok_unused == NULL)
		intf->sok_unused = pqueue_init(NULL,0);
	
	for(i = intf->port; i < (intf->port + intf->no_ports); ++i) {
		for(j = 0, found = 0; j < used && !found; ++j) {
			//D("%d == %d",intf->sok_used[j]->port,i);
			if(intf->sok_used[j]->port == i)
				found = 1;
		}
		if(!found) {
			sok_t *s = xalloc(sizeof(sok_t));
			int ret;
			
			//D("i: %d",i);
			
			addr.sin_family = AF_INET;
			if(intf->bind_ip != NULL)
				memcpy(&(addr.sin_addr),&(intf->p_bind_ip),
						sizeof(struct in_addr));
			else
				addr.sin_addr.s_addr = INADDR_ANY;
			addr.sin_port = htons((unsigned short)i);
			s->sok = alloc_socket(&addr);
			if(s->sok == -1) {
				char buffer[64];
				inet_ntop(AF_INET,&(addr.sin_addr),
						buffer,sizeof(buffer));
				LOGTP(L_ERR,"Failed to bind (%s:%d)",buffer,i);
				xfree(s);
				continue;
			}
			ret = listen(s->sok,1);
			if(ret == -1) {
				char buffer[64];
				inet_ntop(AF_INET,&(addr.sin_addr),
						buffer,sizeof(buffer));
				LOGTP(L_ERR,"Failed to listen on (%s:%d)",
						buffer,i);
				close_sok(&(s->sok));
				xfree(s);
				continue;
			}
			s->port		= i;
			s->inuse	= 0;
			s->poll		= NULL;
			intf->sok_used[used++] = s;
			intf->no_bound++;
		} else
			intf->no_bound++;
	}

	for(i = 0; i < intf->no_soks; ++i) {
		if(intf->sok_used[i] != NULL) {
			if(intf->sok_used[i]->inuse == 0) {
				pqueue_push_back(intf->sok_unused,
						intf->sok_used[i]);
				intf->sok_used[i] = NULL;
			}
		}
	}
}

static void recv_rehash_ports(intf_t *intf) {
	struct sockaddr_in addr;
	socklen_t len;
	int ret;
	
	if(intf->root->sok != -1) {
		len = sizeof(addr);
		getsockname(intf->root->sok,
			(struct sockaddr *)&addr,&len);
		if(ntohs(addr.sin_port) != intf->dccserver_port ||
			memcmp(&(intf->p_bind_ip),&(addr.sin_addr),sizeof(struct in_addr)) != 0)
			close_sok(&(intf->root->sok));
		else
			return;
	}

	if(intf->dccserver_port == 0)
		return;
	
	addr.sin_family = AF_INET;
	if(intf->bind_ip != NULL)
		memcpy(&(addr.sin_addr),&(intf->p_bind_ip),
				sizeof(struct in_addr));
	else
		addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(intf->dccserver_port);
	
	ret = alloc_socket(&addr);
	if(ret == -1) {
		char buffer[64];
		inet_ntop(AF_INET,&(addr.sin_addr),buffer,sizeof(buffer));
		LOGTP(L_ERR,"Failed to allocate DCC SERVER socket (%s:%d)",
				buffer,intf->dccserver_port);
		return;
	} else
		intf->root->sok = ret;
	
	ret = listen(intf->root->sok,5);
	if(ret == -1) {
		char buffer[64];
		inet_ntop(AF_INET,&(addr.sin_addr),buffer,sizeof(buffer));
		LOGTP(L_ERR,"Failed to listen on DCC Server socket (%s:%d)",
				buffer,intf->dccserver_port);
		close_sok(&(intf->root->sok));
	}
}

static void load_interface_settings(intf_t *intf) {
	int rehash_ports = 0;
	
	intf->snd_bufsize = intf->interface->snd_bufsize * 1024L;
	if(intf->snd_bufsize == 0)
		intf->snd_bufsize = max_sokbuf(0);

	intf->rcv_bufsize = intf->interface->rcv_bufsize * 1024L;
	if(intf->rcv_bufsize == 0)
		intf->rcv_bufsize = max_sokbuf(1);
	
	LOCK(intf->interface);
	intf->mode = intf->interface->mode;
	if(intf->port != intf->interface->dcc_port) {
		intf->port = intf->interface->dcc_port;
		rehash_ports = 1;
	}
	if(intf->no_ports != intf->interface->no_ports) {
		intf->no_ports = intf->interface->no_ports;
		rehash_ports = 1;
	}
	if(intf->dccserver_port != intf->interface->dccserver_port) {
		intf->dccserver_port = intf->interface->dccserver_port;
		rehash_ports = 1;
	}
	intf->root->c_rate	= intf->interface->bandwidth * 1024L;
	intf->dcc_timeout	= intf->interface->dcc_timeout;
	intf->token_size	= intf->interface->dcc_packet;
	intf->accept_per_sec	= intf->interface->accept_per_sec;
	intf->secure_mode	= intf->interface->secure_mode;
	if(intf->type == INTERFACE_RECV)
		intf->max_size	= intf->interface->recv.max_size;
	
	if(intf->bind_ip == NULL && intf->interface->bind_ip != NULL) {
		if(set_bind_ip(intf,intf->interface->bind_ip) == 0)
			rehash_ports = 1;
	} else if(intf->bind_ip != NULL && intf->interface->bind_ip == NULL) {
		xfree(intf->bind_ip);
		intf->bind_ip = NULL;
	} else if(intf->bind_ip == NULL && intf->interface->bind_ip == NULL) {
		// NOP
	} else if(strcasecmp(intf->bind_ip,intf->interface->bind_ip) != 0) {
		if(set_bind_ip(intf,intf->interface->bind_ip) == 0)
			rehash_ports = 1;
	}

	if(intf->advertise_ip == NULL && 
			intf->interface->advertise_ip != NULL) {
		set_advertise_ip(intf,intf->interface->advertise_ip);
	} else if(intf->advertise_ip != NULL && 
			intf->interface->advertise_ip == NULL) {
		xfree(intf->advertise_ip);
		intf->advertise_ip = NULL;
	} else if(intf->advertise_ip == NULL && 
			intf->interface->advertise_ip == NULL) {
	} else if(strcasecmp(intf->advertise_ip,
				intf->interface->advertise_ip) != 0) {
		set_advertise_ip(intf,intf->interface->advertise_ip);
	}

	UNLOCK(intf->interface);

	if(rehash_ports //|| (intf->no_soks != intf->no_bound)) {
			|| (intf->no_soks != 0 && intf->no_bound == 0)) {
		switch (intf->type) {
			case INTERFACE_RECV:
				recv_rehash_ports(intf);
			case INTERFACE_SEND:
			case INTERFACE_CHAT:
				intf_rehash_ports(intf);
				break;
		}
	}
}


/** Misc **/

/* Assumes that the interface is locked */
static char *calculate_fn(intf_t *intf, const char *file) {
	size_t fn_size = strlen(file);
	char *fn;

	if(intf->interface->recv.dir != NULL)
		fn_size += strlen(intf->interface->recv.dir);
	else
		fn_size += 1;
	
	fn_size += 2; // seperator and terminator

	fn = xalloc(fn_size);
	snprintf(fn,fn_size,"%s/%s",
			intf->interface->recv.dir != NULL ?
			 intf->interface->recv.dir : ".",
			file);

	return fn;
}

static void calculate_resume(intf_t *intf, node_t *n) {
	struct stat sbuf;
	send_t *r = (send_t *)n->data;
	int ret;

	ret = stat(n->file,&sbuf);
	if(ret == 0) {
		n->pos = sbuf.st_size;
	} else // assume that all failures of stat mean the file doesn't exist
		n->pos = 0;

	if(r != NULL) {
		LOCK(r);
		r->pos = n->pos;
		UNLOCK(r);
	}
}	

static int valid_file_size(intf_t *intf, off_t size) {
	int ret = 0;
	
	LOCK(intf->interface);
	if(intf->interface->recv.max_size == 0 ||
		size <= intf->interface->recv.max_size)
		ret = 1;
	UNLOCK(intf->interface);

	return ret;
}

static void stat_update(uint32_t *mib, uint32_t *carry, const uint32_t add) {
	uint32_t i;
	(*carry) += add;
	i = (*carry) / (1024UL * 1024UL);
	(*mib) += i;
	(*carry) -= i * 1024UL * 1024UL;
}

static void update_bandwidth_stats(intf_t *intf) {
	interface_t *i = intf->interface;
	node_t *n = intf->root;
	time_t now = xtime();
	
	LOCK(i);
	stat_update(&i->stat_mib,&i->stat_carry,n->p_rate);
	i->avgspeed[now % AVGSPEED] = n->p_rate;
	if(n->p_rate > i->record_rate)
		i->record_rate = n->p_rate;
	if(n->c_rate != 0 && n->p_rate > n->c_rate)
		n->c_rate -= (n->p_rate - n->c_rate);
	if(i->bandwidth != 0 && n->c_rate <= 0)
		n->c_rate = (i->bandwidth * 256L);
	n->p_rate = 0;
	n->a_rate = 0;
	UNLOCK(i);
}


/** Node Running Functions **/

static inline enum cl_colour_t place_by_rate(node_t *n, node_t *p) {
	//D("n: %p, p: %p, p->c_rate: %ld, p->a_rate: %ld, p->p_rate: %ld",
	//	n,p,p->c_rate,p->a_rate,p->p_rate);
	if(p->c_rate != 0 && p->p_rate >= p->c_rate) {
		//D("red");
		CL_PUSH_BACK(n->red,p);
		return CL_RED;
	} else if(p->a_rate != 0 &&
		p->p_rate >= p->a_rate &&
		n->yellow != NULL) {
		//D("yellow");
		CL_PUSH_BACK(n->yellow,p);
		return CL_YELLOW;
	} else {
		//D("green");
		CL_PUSH_BACK(n->green,p);
		return CL_GREEN;
	}
}

FUNC_TYPE(colour_run) {
	uint32_t tmp;
	node_t *p;
	int i,ret;
	
	//D("n: %p",n);
	
	for(i = n->green->size; i > 0; --i) {
		p = CL_POP_FRONT(n->green);
		tmp = p->p_rate;
		ret = N_RUN(intf,p);
		if(ret == -1)
			CL_PUSH_BACK(n->red,p);
		else if(ret == 0)
			CL_PUSH_BACK(n->white,p);
		else {
			n->p_rate += p->p_rate - tmp;
			place_by_rate(n,p);
			n->last_ret = 1;
			return NS_NOCHANGE;
		}
	}
	
	for(i = n->yellow->size; i > 0; --i) {
		p = CL_POP_FRONT(n->yellow);
		tmp = p->p_rate;
		ret = N_RUN(intf,p);
		if(ret == -1)
			CL_PUSH_BACK(n->red,p);
		else if(ret == 0)
			CL_PUSH_BACK(n->white,p);
		else {
			n->p_rate += p->p_rate - tmp;
			place_by_rate(n,p);
			n->last_ret = 1;
			return NS_NOCHANGE;
		}
	}
	n->last_ret = 0;
	return NS_NOCHANGE;
}

static inline int need_acks(intf_t *intf, node_t *n) {
	return	(n->ack + (off_t)(intf->snd_bufsize * 2) < n->pos ||
		(n->ack < n->pos && ((n->size - n->pos) < (off_t)(intf->snd_bufsize * 2))));
}

FUNC_TYPE(send_run) {
	uint32_t ackpos = 0;
	ssize_t sret;
	int ret;
	
	while((ret = recv(n->sok,&ackpos,sizeof(ackpos),0)) == sizeof(ackpos)) {
		ackpos = ntohl(ackpos);
		if(n->size > (off_t)UINT32_MAX) { // we need 64bit DCC
			off_t ack64 = ((n->ack / (off_t)UINT32_MAX) 
					* (off_t)UINT32_MAX) + ackpos;
			if(ack64 < n->ack) {
				ack64 += UINT32_MAX -
					((n->ack / (off_t)UINT32_MAX)
					 * (off_t)UINT32_MAX);
			}
			if(ack64 <= n->ack || ack64 > n->pos) { // 64bit
				n->err = ESPIPE;
				goto out;
			} else // 64bit
				n->ack = ack64;
		} else if(ackpos <= n->ack || ackpos > n->pos) { // 32bit
			// something weird!
			n->err = ESPIPE;
			goto out;
		} else // 32bit
			n->ack = (off_t) ackpos;
	}
	if(errno != 0 && errno != EAGAIN) {
		n->err = errno;
		goto out;
	}
	
	if(need_acks(intf,n)) {
		// this implements DCC FAST send of a kind switches to
		// RFC dcc in the last part of the file
		if(n->last_ret == 0 && (n->events & POLLIN) && ackpos == 0) // anti-spin
			n->events = 0;
		else
			n->events = POLLIN;
		n->last_ret = 0;
	} else if (n->pos < n->size) {
		//D("sok:%d, fd:%d, token_size:%d",n->sok,n->fd,n->token_size);
		sret = l_sendfile(n->sok,n->fd,&(n->pos),intf->token_size);
		//D("sret: %ld",sret);
		if(sret <= 0 && errno != EAGAIN) {
			n->err = errno;
			goto out;
		} else if(sret <= 0 && errno == EAGAIN) {
			if(n->events == POLLOUT && n->last_ret == 0) {
				/* check we didn't hit the end of the file */
				off_t eofpos = lseek(n->fd,0,SEEK_END);
				lseek(n->fd,n->pos,SEEK_SET);
				if(n->pos == eofpos || eofpos == (off_t)-1) {
					n->err = EIO;
					goto out;
				} else
					n->events = POLLOUT;
			} else
				n->events = POLLOUT;
			n->last_ret = 0;
		} else {
			n->p_rate += sret;
			n->last_ret = 1;
		}
	} else if(n->ack < n->pos) {
		// end of file
		// shutdown(n->sok,SHUT_WR); // FIXME: ponder this one!
		D("ack: %lld, pos: %lld",n->ack,n->pos);
		n->events = POLLIN;
		n->last_ret = 0;
	} else {
		n->last_ret = -1;
	}
	
out:
	if(n->err != 0) {
		E(n->err);
		n->last_ret = -1;
	}
	return NS_NOCHANGE;
}

FUNC_TYPE(recv_run) {
	uint32_t ackpos;
	ssize_t sret;
	int ret = 1;
	
	if(n->ack < n->pos) {
		ackpos = htonl((uint32_t)n->pos);
		sret = send(n->sok,&ackpos,sizeof(ackpos),0);
		if(sret <= 0) {
			if(errno == EAGAIN) {
				n->events = POLLOUT;
				ret = 0;
			} else {
				n->err = errno;
				ret = -1;
			}
		} else
			n->ack = n->pos;
	}
	if(ret == 1) {
		sret = recv(n->sok, intf->buffer, intf->bufsize, 0);
		if(sret > 0) {
			n->p_rate += sret;
			sret = write(n->fd, intf->buffer, sret);
			if(sret > 0) {
				n->pos += sret;
				ackpos = htonl((uint32_t)n->pos);
				sret = send(n->sok,&ackpos,sizeof(ackpos),0);
				if(sret > 0) {
					n->ack = n->pos;
					n->events = POLLIN;
				} else if(errno == EAGAIN) {
					n->events = POLLOUT;
					ret = 0;
				} else {
					n->err = errno;
					ret = -1;
				}
			} else {
				n->err = errno;
				ret = -1;
			}
		} else if(errno == EAGAIN) {
			if(n->last_ret != 0 && n->events == POLLIN) // anti-spin
				n->events = POLLIN;
			else
				n->events = 0;
			ret = 0;
		} else {
			n->err = errno;
			ret = -1;
		}
	}

	n->last_ret = ret;
	return NS_NOCHANGE;
}

FUNC_TYPE(chat_run) {
	chat_t *c = (chat_t *)n->data;
	size_t len,rem = 512;
	char *ptr;
	int tmp, ret;
	
	/* Lack of locking the chat is intentional, access to the message
	 * queue is locked by the pqueue functions so this should all be ok.
	 */
	if((n->events & POLLOUT) && n->complete != 2) {
		while(rem > 0) {
			if(!linebuf_inuse(n->snd_buf)) {	
				if(pqueue_length(&c->msgqueue) <= 0) {
					if(n->complete == 1) {
						n->complete = 2;
						n->events = 0;
						shutdown(n->sok,SHUT_RDWR);
					} else
						n->events = POLLIN;
					break;
				}	
			
				ptr = pqueue_pop_front(&(c->msgqueue));
				len = strlen(ptr);
			
				if(len > 510)
					len = 510;
			
				if(len > (rem-2)) {
					pqueue_push_front((&c->msgqueue),ptr);
					break;
				}
			
				load_linebuf(n->snd_buf,ptr);
				rem -= len;
			}
			
			ret = send_line(n->sok,n->snd_buf);
			if(ret == -1) {
				if(errno != EAGAIN && errno != EINTR) {
					n->err = errno;
					E(n->err);
				}
				break;
			} else if(ret == 0)
				break;
		}
		if(n->err != 0) {
			n->last_ret = -1;
		} else if(rem != 512) {
			n->p_rate += 512 - rem;
			n->last_ret = 1;
		} else {
			n->last_ret = 0;
		}
	} else if(n->complete == 0) {
		ret = recv_line(n->sok,n->rcv_buf);
		if(ret > 0) {
			char buffer[514];
			size_t len;
			
			len = copy_linebuf(n->rcv_buf,buffer,sizeof(buffer));
			n->p_rate += len;
			
			tmp = chat_list_cmd(c,buffer);
			if(tmp == -1) {
				n->complete = 2;
				shutdown(n->sok,SHUT_RDWR);
				n->events = 0;
				n->last_ret = -1;
			} else if(tmp == 1) {
				n->complete = 1;
				shutdown(n->sok,SHUT_RD);
				n->events = POLLOUT;
				n->last_ret = 1;
			} else if(pqueue_length(&c->msgqueue) > 0) {
				n->events = POLLOUT;
				n->last_ret = 1;
			}
		} else if(ret == -1 && errno != EAGAIN) {
			n->err = errno;
			E(n->err);
			n->last_ret = -1;
		} else {
			if(!(n->last_ret == 0 && n->events == POLLIN))
				n->events = POLLIN;
			else
				n->events = 0; // anti-spin
			n->last_ret = 0;
		}
	} else { // complete = 2
		n->events = 0;
		n->last_ret = -1;
	}

	return NS_NOCHANGE;
}

FUNC_TYPE(common_run_prepoll) {
	node_t *pn;
	
	if((pn = CL_FRONT(n->white)) != NULL) {
		do {
			if(pn->state == NS_RUN && pn->sok >= 0)
				pn->poll = poll_add(intf,pn->sok,pn->events);
			pn = pn->next;
		} while(pn != CL_FRONT(n->white));
	}

	return NS_NOCHANGE;
}

FUNC_TYPE(common_run_postpoll) {
	node_t *pn;
	int i;

	//D("n: %p, n->state: %d",n,n->state);
	
	for(i = n->white->size; i > 0; --i) {
		pn = CL_POP_FRONT(n->white);
		if(pn->state == NS_RUN && pn->sok >= 0) {
			if(pn->poll->revents != 0)
				place_by_rate(n,pn);
			else
				CL_PUSH_BACK(n->white,pn);
		} else if(pn->state == NS_RUN) {
			if(pn->green->size > 0 || pn->yellow->size > 0)
				place_by_rate(n,pn);
			else if(pn->white->size > 0)
				CL_PUSH_BACK(n->white,pn);
			else
				CL_PUSH_BACK(n->red,pn);
		} else
			CL_PUSH_BACK(n->white,pn);
	}

	/*
	D("green: %d, yellow: %d, red: %d, white: %d",
			n->green->size,
			n->yellow->size,
			n->red->size,
			n->white->size);
	*/

	return NS_NOCHANGE;
}


/** Connect Functions **/

FUNC_TYPE(dcc_listen_prepoll) {
	struct sockaddr_in addr;
	socklen_t len = sizeof(addr);
	
	if(n->complete == 0) {
		struct in_addr *adv = NULL;
		
		n->sok = get_listening_socket(intf);
		if(n->sok == -1 && errno == EAGAIN) {
			return NS_NOCHANGE;
		} else if(n->sok == -1) {
			n->err = errno;
			E(n->err);
			return NS_DELETE;
		}
		if(intf->advertise_ip != NULL)
			adv = &(intf->p_advertise_ip);
		
		getsockname(n->sok,(struct sockaddr *)&addr,&len);
		if(intf->type == INTERFACE_SEND)
			dcc_send_msg(adv,(send_t *)n->data,
					ntohs(addr.sin_port));
		else if(intf->type == INTERFACE_RECV)
			dcc_rsend_accept_msg(adv,(send_t *)n->data,
					ntohs(addr.sin_port));
		else if(intf->type == INTERFACE_CHAT)
			dcc_chat_msg(adv,(chat_t *)n->data,
					ntohs(addr.sin_port));
		n->complete = 1;
		n->poll = poll_add(intf,n->sok,POLLIN);
		return NS_NOCHANGE;
	} else if(n->complete == 1) {
		n->poll = poll_add(intf,n->sok,POLLIN);
		return NS_NOCHANGE;
	} else if(n->complete == 2) {
		char host[256],**hosts = NULL;
		int i = 0,ret = 0,found = 0;

		host[0] = '\0';
		
		getpeername(n->fd,(struct sockaddr *)&addr,&len);
		ret = aresolv_lookup_reverse(global->aresolver,
				&(addr.sin_addr),&hosts);
		D("ret: %d",ret);
		if(ret == 0)
			return NS_NOCHANGE;

		if(intf->type == INTERFACE_SEND || 
				intf->type == INTERFACE_RECV) {
			send_t *s = (send_t *)n->data;
			if(s->host != NULL)
				xstrncpy(host,s->host,sizeof(host));
		} else if(intf->type == INTERFACE_CHAT) {
			chat_t *c = (chat_t *)n->data;
			if(c->host != NULL)
				xstrncpy(host,c->host,sizeof(host));
		}
		
		if(host[0] == '\0') {
			xfree(hosts);
			return NS_NOCHANGE;
		}
		
		if(ret > 0) {
			for(i = 0; i < ret && !found; ++i) {
				if(strcasecmp(hosts[i],host) == 0)
					found = 1;
			}
		}
		if(!found) {
			char addrbuf[64];
			inet_ntop(AF_INET,&(addr.sin_addr),addrbuf,sizeof(addrbuf));
			if(strcasecmp(addrbuf,host) == 0)
				found = 1;
		}

		if(hosts != NULL)
			xfree(hosts);

		if(!found) {
			log_unverifiable(intf,n,&addr);
			close_sok(&(n->fd));
			n->complete = 1;
			return NS_NOCHANGE;
		} else {
			log_verified(intf,n,&addr);
			close_socket(intf,&(n->sok));
			n->sok = n->fd;
			n->fd = -1;
			n->complete = 0;
			return NS_NEGOTIATE;
		}
	}
	return NS_DELETE;
}

FUNC_TYPE(dcc_listen_postpoll) {
	if(n->complete == 1) {
		struct sockaddr_in addr;
		socklen_t len = sizeof(addr);
		
		if(n->poll->revents & POLL_ERRORS) {
			n->err = socket_error(n->sok);
			E(n->err);
			return NS_DELETE;
		} else if(n->poll->revents & POLLIN) {
			n->fd = accept_socket(n->sok,
					(struct sockaddr *)&addr,&len);
			if(n->fd != -1) {
				if(intf->secure_mode) {
					n->complete = 2;
				} else {
					n->complete = 0;
					close_socket(intf,&(n->sok));
					n->sok = n->fd;
					n->fd = -1;
					log_unverified(intf,n,&addr);
					return NS_NEGOTIATE;
				}
			}
		}
	}
	return NS_NOCHANGE;
}

FUNC_TYPE(dccserv_conn_prepoll) {
	if(n->sok == -1) {
		struct sockaddr_in addr;
		struct in_addr *addrs;
		char host[256];
		int ret;

		host[0] = '\0';

		if(intf->type == INTERFACE_SEND) {
			send_t *s = (send_t *)n->data;
			LOCK(s);
			if(s->host != NULL)
				xstrncpy(host,s->host,sizeof(host));
			UNLOCK(s);
		} else if(intf->type == INTERFACE_CHAT) {
			chat_t *c = (chat_t *)n->data;
			LOCK(c);
			if(c->host != NULL)
				xstrncpy(host,c->host,sizeof(host));
			UNLOCK(c);
		}
		
		if(host[0] == '\0')
			return NS_NOCHANGE;

		ret = aresolv_lookup(global->aresolver,host,&addrs);

		if(ret == -1) {
			n->err = EDESTADDRREQ;
			E(n->err);
			return NS_DELETE;
		} else if(ret == 0)
			return NS_NOCHANGE;
		
		addr.sin_family = AF_INET;
		addr.sin_port = htons(intf->dccserver_port);
		memcpy(&(addr.sin_addr),&(addrs[0]),sizeof(struct in_addr));
		xfree(addrs);
		
		if(do_connect(intf,n,&addr) == -1)
			return NS_DELETE;
		
	}
	n->poll = poll_add(intf,n->sok,POLLIN | POLLOUT);
	return NS_NOCHANGE;
}

FUNC_TYPE(dcc_outbound_postpoll) {
	D("%d %d %p %p",ct,ft,intf,n);
	if(n->sok != -1) {
		if(n->poll->revents & POLL_ERRORS) {
			n->err = socket_error(n->sok);
			E(n->err);
			return NS_DELETE;
		} else if(n->poll->revents & (POLLIN | POLLOUT)) {
			n->complete = 0;
			return NS_NEGOTIATE;
		}
	}
	return NS_NOCHANGE;
}

FUNC_TYPE(dcc_outbound_prepoll) {
	D("%d %d %p %p",ct,ft,intf,n);
	
	if(n->sok == -1 && n->complete == 0) {
		if(intf->type == INTERFACE_SEND)
			dcc_rsend_msg((send_t *)n->data);
		else if(intf->type == INTERFACE_CHAT)
			dcc_rchat_msg((chat_t *)n->data);
		else if(intf->type == INTERFACE_RECV)
			dcc_recv_msg((send_t *)n->data);
		n->complete = 1;
	} else if(n->sok == -1 && n->complete == 1) {
		struct sockaddr_in addr;
		dcc_aux_t *aux = NULL;

		addr.sin_family = AF_INET;
		
		if(intf->type == INTERFACE_SEND) {
			send_t *s = (send_t *)n->data;
			LOCK(s);
			aux = (dcc_aux_t *)s->aux;
		} else if(intf->type == INTERFACE_CHAT) {
			chat_t *c = (chat_t *)n->data;
			LOCK(c);
			aux = (dcc_aux_t *)c->aux;
		} else if(intf->type == INTERFACE_RECV) {
			send_t *r = (send_t *)n->data;
			LOCK(r);
			if(r->state != STATE_RESUME)
				aux = (dcc_aux_t *)r->aux;
		}
		if(aux != NULL) {
			memcpy(&(addr.sin_addr),&(aux->addr),
					sizeof(struct in_addr));
			addr.sin_port = htons(aux->port);
		}
		if(intf->type == INTERFACE_SEND || intf->type == INTERFACE_RECV)
			UNLOCK((send_t *)n->data);
		else if(intf->type == INTERFACE_CHAT)
			UNLOCK((chat_t *)n->data);
		
		if(aux == NULL)
			return NS_NOCHANGE;
		else if(do_connect(intf,n,&addr) == -1)
			return NS_DELETE;
		
		n->complete = 0;
		n->poll = poll_add(intf,n->sok,POLLOUT | POLLIN);
	} else {
		n->poll = poll_add(intf,n->sok,POLLOUT);
	}
	return NS_NOCHANGE;
}


/** Negotiate Functions **/

FUNC_TYPE(send_nego_prepoll) {
	send_t *s = (send_t *)n->data;

	LOCK(s);
	s->state	= STATE_ACTIVE;
	s->bytes_sent	= 0;
	s->connected	= xtime();
	s->last_contact	= s->connected;
	n->pos		= s->pos;
	n->ack		= n->pos;
	UNLOCK(s);

	n->events 	= POLLOUT;
	
	set_sndbuf(intf,n->sok);
	
	n->fd = open(n->file,O_RDONLY | O_LARGEFILE);

	if(n->fd != -1) {
		log_connected(intf,n);
		se_notify_send(1,intf->interface,s,0);
		return NS_RUN;
	} else {
		n->err = errno;
		E(n->err);
		return NS_DELETE;
	}
}

FUNC_TYPE(send_chat_dccserv_nego_prepoll) {
	n->poll = poll_add(intf,n->sok,n->complete == 0 ? POLLOUT : POLLIN);
	return NS_NOCHANGE;
}

static void get_network_nick(send_t *s, chat_t *c, 
		char *buffer, size_t bufsize) {
	network_t *net = NULL;

	if(s != NULL) {
		LOCK(s);
		net = s->network;
		UNLOCK(s);
	} else if(c != NULL) {
		LOCK(c);
		net = c->network;
		UNLOCK(c);
	}

	if(net == NULL)
		return;

	LOCK(net);
	if(net->info != NULL) {
		LOCK(net->info);
		xstrncpy(buffer,net->info->nick,sizeof(buffer));
		UNLOCK(net->info);
	}
	UNLOCK(net);
}

FUNC_TYPE(send_dccserv_nego_postpoll) {
	char		buffer[514];
	send_t 		*s = (send_t *)n->data;
	int 		ret = -1;
	
	if(n->poll->revents & POLL_ERRORS) {
		n->err = socket_error(n->sok);
		E(n->err);
		return NS_DELETE;				
	} else if(n->complete == 0 && (n->poll->revents & POLLOUT)) {
		char nick[64] = "ARISA";

		get_network_nick(s,NULL,nick,sizeof(nick));
		pathtofn(n->file,intf->buffer,intf->bufsize);
		
		ret = IMIN(
			snprintf(buffer,513,
				"120 %s %lld %s\r\n",
				nick,n->size,intf->buffer),
			512
		);

		ret = send(n->sok,buffer,ret,0);
		if(ret == -1 && (errno == EAGAIN || errno == EINTR))
			return NS_NOCHANGE;
		else if(ret == -1) {
			n->err = errno;
			E(n->err);
			return NS_DELETE;
		}
		
		n->complete = 1;
	} else if(n->complete == 1 && (n->poll->revents & POLLIN)) {
		char *ptr;
		
		ret = recv(n->sok,buffer,ret,0);
		if(ret == -1 && (errno == EAGAIN || errno == EINTR))
			return NS_NOCHANGE;
		else if(ret == -1 || ret == 0) {
			n->err = errno;
			E(n->err);
			return NS_DELETE;
		}
		buffer[ret] = '\0';

		ret = strtol(buffer,NULL,10);
		ptr = strchr(buffer,' ');
		if(ptr != NULL)
			n->pos = strtoull(ptr + 1,NULL,10);

		if(ret != 121 || n->pos >= n->size) {
			if(ret != 121)
				n->err = EPROTONOSUPPORT;
			else
				n->err = ERANGE;
			E(n->err);
			return NS_DELETE;
		}

		LOCK(s);
		s->pos = n->pos;
		UNLOCK(s);

		set_func(n,NEGO_PREPOLL,send_nego_prepoll);
		set_func(n,NEGO_POSTPOLL,NULL);
	}
	
	return NS_NOCHANGE;
}

FUNC_TYPE(recv_nego_prepoll) {
	char buffer[4096];
	struct utimbuf ubuf;
	struct stat sbuf;
	send_t *r = (send_t *)n->data;
	int ret;
	
	if(n->file == NULL) {
		n->err = EINVAL;
		E(n->err);
		return NS_DELETE;
	}
	
#ifdef HAVE_STATFS
	{
		struct statfs sfsbuf;
		
		LOCK(intf->interface);
		if(intf->interface->recv.dir != NULL)
			xstrncpy(buffer,intf->interface->recv.dir,sizeof(buffer));
		else
			xstrncpy(buffer,".",sizeof(buffer));
		UNLOCK(intf->interface);
		
		ret = statfs(buffer,&sfsbuf);
		if(ret == 0) {
			if((n->size - n->pos) >
					((off_t)sfsbuf.f_bsize * 
					 (off_t)sfsbuf.f_bavail)) {
				n->err = ENOMEM;
				E(n->err);
				return NS_DELETE;
			}
		}
	}
#endif /* HAVE_STATFS */
	
	if(n->pos == 0) {
		n->fd = open(n->file,
				O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE,
				S_IRUSR | S_IWUSR | S_IRGRP);
	} else {
		ret = stat(n->file,&sbuf);
		if(ret == -1) {
			n->err = errno;
			E(n->err);
			return NS_DELETE;
		} else if((xtime() - sbuf.st_mtime) < 5) {
			n->err = EBUSY;
			E(n->err);
			return NS_DELETE;
		} else if(sbuf.st_size < n->pos) {
			n->err = EIO;
			E(n->err);
			return NS_DELETE;
		}
		n->fd = open(n->file,O_WRONLY | O_LARGEFILE);
	}
	
	if(n->fd == -1) {
		n->err = errno;
		return NS_DELETE;
	}

	/* Set the modification time now so that if by chance we attempt to
	 * resume this file multiple times at once further attempts will fail.
	 */
	ubuf.actime = ubuf.modtime = xtime();
	utime(n->file,&ubuf); // ignore errors
	
	if(n->pos != 0) {
		if(sbuf.st_size > n->pos)
			ftruncate(n->fd,n->pos); // ignore errors
		if(lseek(n->fd,n->pos,SEEK_SET) == -1) {
			n->err = errno;
			E(n->err);
			return NS_DELETE;
		}
	}

	LOCK(r);
	r->state = STATE_ACTIVE;
	if(r->connected == 0)
		r->connected = xtime();
	r->last_contact = xtime();
	r->pos = n->pos;
	se_notify_recv(0,intf->interface,r,0);
	UNLOCK(r);
	
	set_rcvbuf(intf,n->sok);
	n->state = NS_RUN;
	n->events = POLLIN;
	log_upload_active(intf,n);
	
	return NS_RUN;
}

FUNC_TYPE(recv_dccserv_nego_prepoll) {
	n->poll = poll_add(intf,n->sok,n->events);
	return NS_NOCHANGE;
}

static int recv_dccserv_init_upload(intf_t *intf, node_t *n); // pre-declaration
FUNC_TYPE(recv_dccserv_nego_postpoll) {
	char buffer[514];
	int ret;
	
	if(n->poll == NULL)
		return NS_NOCHANGE;
	
	if(n->poll->revents & POLL_ERRORS) {
		// continue
	} else if(n->complete == 0 && (n->poll->revents & POLLIN)) {
		char *ptr, **tokens = NULL;
		int no_tokens = 4;
		
		ret = recv(n->sok,buffer,512,0);
		if(ret == -1 && ret == EAGAIN)
			return NS_NOCHANGE;
		else if(ret == -1 || ret == 0)
			return NS_DELETE;
		
		buffer[ret] = '\0';
		ptr = strchr(buffer,'\r');
		if(ptr != NULL)
			*ptr = '\0';
		else {
			ptr = strchr(buffer,'\n');
			if(ptr != NULL)
				*ptr = '\0';
		}

		tokens = split_opt(buffer,buffer,&no_tokens);

		ret = NS_DELETE;
		if(no_tokens == 4) {
			if(atoi(tokens[0]) == 120) {
				LOCK(intf->interface);
				n->file = calculate_fn(intf,tokens[3]);
				UNLOCK(intf->interface);
				n->size = strtoull(tokens[2],NULL,10);
				if(valid_file_size(intf,n->size)) {
					calculate_resume(intf,n);
					ret = NS_NOCHANGE;
				}
			}
		} else if(no_tokens == 2) {
			if(atoi(tokens[0]) == 100) {
				ret = NS_NOCHANGE;
			}
		}
		if(tokens != NULL)
			xfree(tokens);
		if(ret != NS_DELETE) {
			n->complete = 1;
			n->events = POLLOUT;
			return ret;
		}
	} else if(n->complete == 1) {
		struct sockaddr_in addr;
		socklen_t len = sizeof(addr);
		char buffer[64],**hosts;
		int i,ret,found = 0;
		
		getpeername(n->sok,(struct sockaddr *)&addr,&len);
		ret = aresolv_lookup_reverse(global->aresolver,
				&(addr.sin_addr),&hosts);
		if(ret == 0)
			return NS_NOCHANGE;
		
		inet_ntop(AF_INET,&(addr.sin_addr),buffer,sizeof(buffer));
		
		if(n->file != NULL) {
			access_t *acc;
			
			LOCK(intf->interface);
			acc = intf->interface->recv.access;
			UNLOCK(intf->interface);
			
			if(ret > 0) {
				for(i = 0; i < ret && !found; ++i) {
					if(access_check(acc,NULL,hosts[i],NULL,NULL))
						found = 1;
				}
			}
			if(!found) {
				if(access_check(acc,NULL,buffer,NULL,NULL))
					found = 1;
			}
		} else {
			LOCK(global);
			for(i = 0; i < global->no_users && !found; ++i) {
				user_t *u = global->users[i];
				int j,k;

				LOCK(u);
				for(j = 0; j < u->no_hosts && !found; ++j) {
					for(k = 0; k < ret && !found; ++k) {
						if(irc_hostmask_match(hosts[k],u->hosts[j]))
							found = 1;
					}
					if(!found) {
						if(irc_hostmask_match(buffer,u->hosts[j]))
							found = 1;
					}
				}
				UNLOCK(u);
			}
			UNLOCK(global);
		}
		if(found) {
			n->complete = 2;
			return NS_NOCHANGE;
		}
	} else if(n->complete == 2 && (n->poll->revents & POLLOUT)) {
		if(n->file != NULL) {
			ret = IMIN(
				snprintf(buffer,sizeof(buffer),
					"121 %lld",n->pos),
				sizeof(buffer)-1
			);
			if(recv_dccserv_init_upload(intf,n) == 0) {
				ret = send(n->sok,buffer,ret,0);
				if(ret != -1)
					return NS_NOCHANGE;
			}
		} else {
			xstrncpy(buffer,"101 ARISA\r\n",sizeof(buffer));
			ret = send(n->sok,buffer,strlen(buffer),0);
			if(ret != -1) {
				admin_open(n->sok,NULL,NULL,NULL,NULL);
				n->sok = -1;
			}
			return NS_DELETE;
		}
	} else if(n->poll->revents == 0)
		return NS_NOCHANGE;

	xstrncpy(buffer,"150 Unavailable\r\n",sizeof(buffer));
	send(n->sok,buffer,strlen(buffer),0);
	return NS_DELETE;
}

FUNC_TYPE(chat_nego_prepoll) {
	chat_t *c = (chat_t *)n->data;
	interface_t *i = intf->interface;
	
	LOCK(i);
	LOCK(c);
	if(c->type == CHAT_ADMIN) {
		PTRARR_DEL(&(i->chat.chats),&(i->chat.no_chats),c);
		UNLOCK(c);
		UNLOCK(i);
		admin_begin(n->sok,c);
		n->sok = -1;
		n->data = NULL;
	} else if(c->type == CHAT_LIST) {
		packlist_t *list = c->list.packlist;
		c->connected = xtime();
		c->last_contact = c->connected;
		UNLOCK(c);
		UNLOCK(i);
		if(packlist_valid(list)) {
			LOCK(list);
			n->time_limit 		= list->time_limit;
			UNLOCK(list);
			n->events		= POLLOUT;
			n->snd_buf		= alloc_linebuf(512);
			n->rcv_buf		= alloc_linebuf(512);
			n->state		= NS_RUN;
			n->complete		= 0;
			log_connected(intf,n);
			return NS_RUN;
		}
	} else {
		UNLOCK(c);
		UNLOCK(i);
	}
	
	return NS_DELETE;
}

FUNC_TYPE(chat_dccserv_nego_postpoll) {
	char buffer[514];
	chat_t *c = (chat_t *)n->data;
	int ret = -1;
	
	if(n->poll->revents & POLL_ERRORS) {
		n->err = socket_error(n->sok);
		E(n->err);
		return NS_DELETE;
	} else if(n->complete == 0 && (n->poll->revents & POLLOUT)) {
		char nick[64] = "ARISA";
		get_network_nick(NULL,c,nick,sizeof(nick));

		ret = IMIN(
			snprintf(buffer,513,"100 %s",nick),
			512
		);

		ret = send(n->sok,buffer,ret,0);
		if(ret == -1 && (errno == EAGAIN || errno == EINTR))
			return NS_NOCHANGE;
		else if(ret == -1) {
			n->err = errno;
			E(n->err);
			return NS_DELETE;
		}

		n->complete = 1;
	} else if(n->complete == 1 && (n->poll->revents & POLLIN)) {
		ret = recv(n->sok,buffer,512,0);
		if(ret == -1 && (errno == EAGAIN || errno == EINTR))
			return NS_NOCHANGE;
		else if(ret == -1) {
			n->err = errno;
			E(n->err);
			return NS_DELETE;
		}
		buffer[ret] = '\0';

		ret = strtol(buffer,NULL,10);

		if(ret != 101) {
			n->err = EPROTONOSUPPORT;
			E(n->err);
			return NS_DELETE;
		}

		set_func(n,NEGO_PREPOLL,chat_nego_prepoll);
		set_func(n,NEGO_POSTPOLL,NULL);
	}

	return NS_NOCHANGE;
}


/** Bandwidth Functions **/

static int active_nodes(cl_t *cl) {
	node_t *n = CL_FRONT(cl);
	int count = 0;
	
	if(n == NULL)
		return 0;
	do {
		if(n->state == NS_RUN)
			count++;
		n = n->next;
	} while(n != CL_FRONT(cl));

	return count;
}

static int active_subnodes(node_t *n) {
	return 	active_nodes(n->green)	+ active_nodes(n->yellow) + 
		active_nodes(n->red)	+ active_nodes(n->white);
}

static int has_active(node_t *n); // pre-declaration
static int has_active_cl(cl_t *cl) {
	node_t *n;
	
	if(cl == NULL)
		return 0;
	n = CL_FRONT(cl);
	if(n == NULL)
		return 0;

	do {
		if(n->state == NS_RUN && n->sok >= 0)
			return 1;
		else if(has_active(n))
			return 1;
		n = n->next;
	} while(n != CL_FRONT(cl));
	
	return 0;
}

static int has_active(node_t *n) {
	if(has_active_cl(n->green))
		return 1;
	if(has_active_cl(n->yellow))
		return 1;
	if(has_active_cl(n->red))
		return 1;
	if(has_active_cl(n->white))
		return 1;
	return 0;
}

static void assign_ac_rates(cl_t *cl, uint32_t a_rate, uint32_t c_rate) {
	node_t *n = CL_FRONT(cl);
	if(n == NULL)
		return;
	do {
		n->a_rate = a_rate;
		if(c_rate != 0 && (n->c_rate > c_rate || n->c_rate == 0))
			n->c_rate = c_rate;
		n = n->next;
	} while(n != CL_FRONT(cl));
}

static uint32_t sum_rates(cl_t *cl) {
	node_t *n = CL_FRONT(cl);
	uint32_t sum = 0;
	
	if(n == NULL)
		return 0;
	do {
		if(n->state == NS_RUN) {
			if(n->c_rate != 0)
				sum += n->c_rate;
			else
				return -1;
		}
		n = n->next;
	} while(n != CL_FRONT(cl));

	return sum;
}

FUNC_TYPE(calculate_rates) {
	uint32_t tmp, rate;
	int sub_nodes = active_subnodes(n);
	
	if(n->a_rate == 0) {
		if(n->c_rate == 0) {
			rate = 0;
			rate = rate != -1 && 
				(tmp = sum_rates(n->green))	!= -1 ? rate + tmp : -1;
			rate = rate != -1 &&
				(tmp = sum_rates(n->yellow))	!= -1 ? rate + tmp : -1;
			rate = rate != -1 &&
				(tmp = sum_rates(n->red)) 	!= -1 ? rate + tmp : -1;
			rate = rate != -1 &&
				(tmp = sum_rates(n->white))	!= -1 ? rate + tmp : -1;
			if(rate < 0)
				rate = 0;
		} else
			rate = n->c_rate;
	
		n->a_rate = rate;
	} else
		rate = n->a_rate;

	if(rate != 0 && sub_nodes > 0)
		rate /= sub_nodes;
	
	assign_ac_rates(n->green,rate,n->c_rate);
	assign_ac_rates(n->yellow,rate,n->c_rate);
	assign_ac_rates(n->red,rate,n->c_rate);
	assign_ac_rates(n->white,rate,n->c_rate);

	return NS_NOCHANGE;
}

/** Delete Functions **/

FUNC_TYPE(recv_dccserv_delete) {
	if(n->sok != -1)
		log_dccserv_close(intf,n);

	return NS_DELETE;
}

static void send_delete_msg(char *buffer, size_t bufsize,
		node_t *n,
		state_t state, char *netnick, char *file,
		int hashed, char *crc, char *md5,
		off_t sent, off_t rate,
		time_t now, time_t elapsed,
		char *reason) {
	if(state == STATE_COMPLETE) {
		if(hashed)
			snprintf(buffer,bufsize,
				"Send of \"%s\" Completed, sent %lld bytes at %uKiB/sec. CRC: %s. MD5: %s.",
				file,sent,
				(uint32_t)(sent / (elapsed != 0 ? elapsed : 1)) / 1024,
				crc,md5);
		else
			snprintf(buffer,bufsize,
				"Send of \"%s\" Completed, sent %lld bytes at %uKiB/sec.",
				file,sent,
				(uint32_t)(sent / (elapsed != 0 ? elapsed : 1)) / 1024);
		LOGTP(L_INT,"Closed Send to [%s] of {\"%s\"}, Completed, %lld bytes sent at %uKiB/sec.",
				netnick,file,sent,
				(uint32_t)((sent / (elapsed != 0 ? elapsed : 1)) / 1024));
	} else if(state == STATE_DELETED) {
		snprintf(buffer,bufsize,
			"Closed Send of \"%s\", %s.",
			file,
			reason != NULL ? reason : "Admin Requested Close");
		LOGTP(L_INT,
			"Closed Send to [%s] of {\"%s\"}, Deleted (Reason: %s).",
			netnick,file,
			reason != NULL ? reason : "Admin Requested");
	} else if(state == STATE_SLOW) {
		snprintf(buffer,bufsize,
			"Closed Send of \"%s\", Speed of %.1fKiB/sec is Below Minimum Requirement.",
			file,(float)(rate / AVGSPEED) / 1024.0);

		LOGTP(L_INT,
			"Closed Send to [%s] of {\"%s\"}, Below Minimum Speed Requirement.",
			netnick,file);
	} else if(state == STATE_TIMEOUT) {
		snprintf(buffer,bufsize,
			"Closed Send of \"%s\", Timed Out.",
			file);
		LOGTP(L_INT,
			"Closed Send to [%s] of {\"%s\"}, Timed Out.",
			netnick,file);
	} else {
		char errbuf[96];
		lstrerror_r(n->err,errbuf,sizeof(errbuf));
		snprintf(buffer,bufsize,
			"Closed Send of \"%s\", Error: %s.",
			file,errbuf);
		LOGTP(L_INT,
			"Closed Send to [%s] of {\"%s\"}, Error: %s.",
			netnick,file,errbuf);
	}
}

static void recv_delete_msg(char *buffer, size_t bufsize,
		node_t *n,
		state_t state, char *netnick, char *file,
		off_t sent,
		time_t now, time_t elapsed,
		char *reason) {
	send_t *send = (send_t *)n->data;
	dcc_aux_t *aux;
	char addrbuf[64];
	
	LOCK(send);
	aux = (dcc_aux_t *)send->aux;
	if(aux != NULL) {
		char addrbuf2[64];
		inet_ntop(AF_INET,&(aux->addr),addrbuf2,sizeof(addrbuf2));
		snprintf(addrbuf,sizeof(addrbuf),"%s:%u",addrbuf2,aux->port);
	}
	UNLOCK(send);
	if(aux == NULL)
		peer_hostport_str(n->sok,addrbuf,sizeof(addrbuf));
	
	if(state == STATE_COMPLETE) {
		snprintf(buffer,bufsize,
			"Upload of \"%s\" Completed, %lld bytes at %uKiB/sec.",
			file,sent,
			(uint32_t)(sent / (elapsed != 0 ? elapsed : 1)) / 1024);
		LOGTP(L_INT,"Closed Upload of {\"%s\"} from (%s) [%s], Completed %lld bytes at %uKiB/sec.",
			file,addrbuf,netnick,sent,
			(uint32_t)(sent / (elapsed != 0 ? elapsed : 1)) / 1024);
	} else if(state == STATE_DELETED) {
		snprintf(buffer,bufsize,
			"Closed Upload of \"%s\", %s.",file,
			reason != NULL ? reason : "Admin Requested Close");
		LOGTP(L_INT,"Closed Upload of {\"%s\"} from (%s) [%s], Deleted (Reason: %s).",
			file,addrbuf,netnick,
			reason != NULL ? reason : "Admin Request");
	} else {
		char errbuf[96];
		lstrerror_r(n->err,errbuf,sizeof(errbuf));
		snprintf(buffer,bufsize,
			"Closed Upload of \"%s\", Error: %s.",
			file, 
			state == STATE_TIMEOUT ? "Timed Out" : errbuf);
		LOGTP(L_INT,"Closed Upload of {\"%s\"} from (%s) [%s], Error: %s.",
			file,addrbuf,netnick,
			state == STATE_TIMEOUT ? "Timed Out" : errbuf);

	}
}

FUNC_TYPE(send_recv_delete) {
	send_t *s = (send_t *)n->data;
	char buffer[512],nick[64],netnick[128],reason[256];
	char file[512],crc[64],md5[64];
	state_t state;
	network_t *net;
	time_t elapsed, now = xtime();
	off_t sent = 0, rate = 0;
	int i,hashed = 0;
	
	netnick_str(intf,n,netnick,sizeof(netnick));
	
	LOCK(s);
	net = s->network;
	xstrncpy(nick,s->nick != NULL ? s->nick : "",sizeof(nick));
	
	state = s->state;
	elapsed = (now - s->connected);
	sent = s->bytes_sent;

	xstrncpy(reason,s->reason != NULL ? s->reason : "",sizeof(reason));
	
	if(intf->type == INTERFACE_SEND) {
		se_notify_send(0,intf->interface,s,n->err);
		if(s->pack != NULL) {
			if(state == STATE_COMPLETE)
				WLOCK(s->pack);
			else
				RLOCK(s->pack);
			pathtofn(s->pack->file,file,sizeof(file));
			if((hashed = s->pack->hashed) != 0) {
				xstrncpy(crc,s->pack->crc,sizeof(crc));
				xstrncpy(md5,s->pack->md5,sizeof(md5));
			}
			if(state == STATE_COMPLETE) {
				s->pack->no_gets++;
				WUNLOCK(s->pack);
			} else {
				RUNLOCK(s->pack);
			}
		} else
			file[0] = '\0';
	
		for(i = 0, rate = 0; i < AVGSPEED; ++i)
			rate += (off_t) s->avgspeed[i];
		
		if(state == STATE_ERROR || state == STATE_TIMEOUT)
			s->state = STATE_PURGEREQUEUE;
		else
			s->state = STATE_PURGING;
	} else {
		RLOCK(s->pack);
		pathtofn(s->pack->file,file,sizeof(file));
		RUNLOCK(s->pack);
		se_notify_recv(0,intf->interface,s,n->err);
	}

	UNLOCK(s);
	
	if(intf->type == INTERFACE_SEND) {
		send_delete_msg(buffer,sizeof(buffer),
				n,state,
				netnick,file,hashed,crc,md5,
				sent,rate,now,elapsed,
				reason[0] != '\0' ? reason : NULL);
		if(state == STATE_COMPLETE && n->pool != NULL) {
			uint32_t avgrate = sent / elapsed;
			LOCK(n->pool);
			if(n->pool->record_send < avgrate)
				n->pool->record_send = avgrate;
			UNLOCK(n->pool);
		}
	} else if(intf->type == INTERFACE_RECV) {
		recv_delete_msg(buffer,sizeof(buffer),
				n,state,
				netnick,file,sent,
				now,elapsed,
				reason[0] != '\0' ? reason : NULL);
	}

	LOCK(s);
	if(s->aux != NULL) {
		xfree(s->aux);
		s->aux = NULL;
	}
	UNLOCK(s);
	
	if(intf->type == INTERFACE_RECV) {
		LOCK(intf->interface);
		PTRARR_DEL(&(intf->interface->recv.uploads),
			&(intf->interface->recv.no_uploads),s);
		UNLOCK(intf->interface);
		free_send(s);
	}

	if(!intf->end && net != NULL)
		irc_send_notice(net,nick,buffer);

	return NS_DELETE;
}

FUNC_TYPE(chat_delete) {
	chat_t *c = (chat_t *)n->data;
	char errbuf[96],netnick[128],why[256];

	if(c == NULL)
		return NS_DELETE;
	
	netnick_str(intf,n,netnick,sizeof(netnick));
	
	LOCK(c);
	switch(c->state) {
		case STATE_COMPLETE:
			strcpy(why,"Complete");
			break;
		case STATE_DELETED:
			if(c->reason == NULL)
				strcpy(why,"Admin Request");
			else
				xstrncpy(why,c->reason,sizeof(why));
			break;
		case STATE_TIMEOUT:
			strcpy(why,"Timed Out");
			break;
		case STATE_ERROR:
			snprintf(why,sizeof(why),"Error: %s",
				lstrerror_r(n->err,errbuf,sizeof(errbuf)));
			break;
		default:
			why[0] = '\0';
			break;
	}
	UNLOCK(c);
	
	LOGTP(L_INT,"Closed chat with [%s], %s.",netnick,why);
	
	LOCK(intf->interface);
	PTRARR_DEL(&(intf->interface->chat.chats),
		&(intf->interface->chat.no_chats),c);
	UNLOCK(intf->interface);
	free_chat(c);

	return NS_DELETE;
}


/** Check Functions **/

FUNC_TYPE(send_recv_check_prepoll) { 
	send_t *send = (send_t *)n->data;
	time_t now = xtime();
	uint32_t speed;
	int i;
	
	//D("1: %p, colour: %d, events: %d",n,n->colour,n->events);
	
	LOCK(send);
	
	if(send->state == STATE_DELETED) {
		UNLOCK(send);
		return NS_DELETE;
	}
	if((n->state != NS_RUN || (n->state == NS_RUN && n->p_rate == 0)) &&
			intf->dcc_timeout != 0 &&
			(send->last_contact + intf->dcc_timeout) < now) {
		send->state = STATE_TIMEOUT;
		UNLOCK(send);
		return NS_DELETE;
	}

	if(n->state == NS_RUN) {
		if(n->colour == CL_RED && n->err != 0) {
			if(n->pos != n->size) {
				send->state = STATE_ERROR;
			} else {
				send->state = STATE_COMPLETE;
				send->bytes_sent += (off_t) n->p_rate;
			}
			UNLOCK(send);
			return NS_DELETE;
		} else if(n->pos == n->size && n->ack == n->size) {
			send->state = STATE_COMPLETE;
			send->bytes_sent += (off_t) n->p_rate;
			UNLOCK(send);
			return NS_DELETE;
		} else if(n->pos == n->size)
			send->state = STATE_COMPLETING;
	
		send->avgspeed[now % AVGSPEED] = n->p_rate;
		if((send->connected + 60) < now && n->min_rate > 0) {
			for(i = 0, speed = 0; i < AVGSPEED; ++i)
				speed += send->avgspeed[i];
			if(speed < (n->min_rate * AVGSPEED)) { 
				send->state = STATE_SLOW;
				UNLOCK(send);
				return NS_DELETE;
			}
		}
		
		send->bytes_sent += (off_t) n->p_rate;
		send->pos = n->pos;
	
		if(n->p_rate > 0)
			send->last_contact = now;
		
		if(send->state != STATE_COMPLETING)
			send->state = STATE_ACTIVE;
		UNLOCK(send);

		n->c_rate = n->max_rate;
		if(n->c_rate != 0 && n->p_rate > n->c_rate) {
			int32_t lc_rate = n->c_rate - (n->p_rate - n->c_rate);
			if(n->max_rate != 0 && lc_rate <= 0)
				lc_rate = n->c_rate / 4;
			if(lc_rate > 0)
				n->c_rate = lc_rate;
		}
		n->p_rate = 0;
		n->a_rate = 0;
	
		if(n->events == 0) {
			if(intf->type == INTERFACE_SEND) {
				if(!need_acks(intf,n) && n->pos < n->size)
					n->events = POLLOUT;
				else
					n->events = POLLIN;
			} else {
				n->events = POLLIN;
			}
		}
	
		if(n->colour != CL_WHITE)
			n->colour = CL_GREEN;
		else
			n->poll = poll_add(intf,n->sok,n->events);
	} else
		UNLOCK(send);

	//D("2: %p, colour: %d, events: %d",n,n->colour,n->events);
	
	return NS_NOCHANGE;
}

FUNC_TYPE(dcc_check_postpoll) {
	if(n->state == NS_RUN && n->colour == CL_WHITE) {
		if(n->poll->revents)
			n->colour = CL_GREEN;
	}

	return NS_NOCHANGE;
}

static void assign_max_min_rate(cl_t *cl, uint32_t max, uint32_t min) {
	node_t *n = CL_FRONT(cl);
	if(n == NULL)
		return;
	do {
		n->max_rate = max;
		n->min_rate = min;
		n = n->next;
	} while(n != CL_FRONT(cl));
}

static void init_send(intf_t *intf, node_t *n, send_t *s); // pre-declaration
FUNC_TYPE(pool_check_prepoll) {
	pool_t *p = (pool_t *) n->data;
	send_t *s;
	uint32_t send_max, send_min;
	time_t now = xtime();
	int i;

	//D("%d %d %p %p",ct,ft,intf,n);
	//D("p: %p",p);
	
	pool_purge_sends(p);
	
	LOCK(p);
	if(p->deleted != 0) {
		UNLOCK(p);
		return NS_DELETE;
	}
	stat_update(&p->stat_mib,&p->stat_carry,n->p_rate);
	p->avgspeed[now % AVGSPEED] = n->p_rate;
	if(n->p_rate > p->record_rate)
		p->record_rate = n->p_rate;
	/* If the pool exceeded its ceiling rate last cycle, compensate with a
	   lower ceiling this cycle. */
	n->c_rate = (p->bandwidth * 1024U);
	if(n->c_rate != 0 && n->p_rate > n->c_rate)
		n->c_rate -= (n->p_rate - n->c_rate);
	if(p->bandwidth != 0 && n->c_rate <= 0)
		n->c_rate = (p->bandwidth * 256U);
	n->p_rate = 0;
	n->a_rate = 0;
	
	send_max = p->send_max_bandwidth * 1024U;
	send_min = p->send_min_bandwidth * 1024U;
	
	assign_max_min_rate(n->green,send_max,send_min);
	assign_max_min_rate(n->yellow,send_max,send_min);
	assign_max_min_rate(n->red,send_max,send_min);
	assign_max_min_rate(n->white,send_max,send_min);
	
	while(p->no_csends < p->sends) {
		UNLOCK(p);
		s = queue_dequeue(p);
		LOCK(p);
		if(s != NULL) {
			PTRARR_ADD(&p->csends,&p->no_csends,s);
			s->state = STATE_QUEUED;
		} else // here since we need lock on
			break; 
	}
	for(i = 0; i < p->no_csends; ++i) {
		LOCK(p->csends[i]);
		if(p->csends[i]->state == STATE_QUEUED) {
			init_send(intf,n,p->csends[i]);
			se_notify_send(0,intf->interface,p->csends[i],0);
		}
		UNLOCK(p->csends[i]);
	}
	UNLOCK(p);

	return NS_NOCHANGE;
}

static void node_shuffle(node_t *n); // pre-declaration
FUNC_TYPE(pool_check_postpoll) {
	node_shuffle(n);
	
	if(n->green->size > 0 || n->yellow->size > 0)
		n->colour = CL_GREEN;
	else if(n->white->size > 0)
		n->colour = CL_WHITE;
	else
		n->colour = CL_RED;

	/*
	D("green: %d, yellow: %d, white: %d, red: %d, n->colour: %d",
		n->green->size,n->yellow->size,
		n->white->size,n->red->size,
		n->colour);
	*/
	return NS_NOCHANGE;
}

FUNC_TYPE(recv_dccserv_check_prepoll) {
	time_t now = xtime();

	if((n->connected + intf->dcc_timeout) < now)
		return NS_DELETE;
	else
		return NS_NOCHANGE;
}

FUNC_TYPE(chat_check_prepoll) {
	chat_t *chat = (chat_t *)n->data;
	time_t connected, now = xtime();
	packlist_t *list;

	LOCK(chat);

	if(chat->state == STATE_DELETED) {
		UNLOCK(chat);
		return NS_DELETE;
	}
	if((n->state != NS_RUN || (n->state == NS_RUN && n->p_rate == 0)) &&
			intf->dcc_timeout != 0 && 
			(chat->last_contact + intf->dcc_timeout) < now) {
		chat->state = STATE_TIMEOUT;
		UNLOCK(chat);
		return NS_DELETE;
	}
	
	if(n->state == NS_RUN) {
		if(n->colour == CL_RED && (n->err != 0 || n->complete == 2)) {
			if(n->complete)
				chat->state = STATE_COMPLETE;
			else
				chat->state = STATE_ERROR;
			UNLOCK(chat);
			D("");
			return NS_DELETE;
		} else if(chat->state == STATE_COMPLETING &&
				(pqueue_length(&(chat->msgqueue)) == 0 ||
				 n->complete == 2)) {
			chat->state = STATE_COMPLETE;
			UNLOCK(chat);
			D("");
			return NS_DELETE;
		} else if(chat->state == STATE_COMPLETING)
			n->events = POLLOUT;
		
		if(n->p_rate != 0)
			chat->last_contact = now;

		n->c_rate = 0;
		n->p_rate = 0;
		n->a_rate = 0;

		if(n->events == 0)
			n->events = POLLIN;

		list = chat->list.packlist;
		connected = chat->connected;
		UNLOCK(chat);

		if(!packlist_valid(list) || (connected + n->time_limit) < now) {
			char *msg;

			LOCK(chat);
			n->events = POLLOUT;
			n->complete = 1;
			chat->state = STATE_COMPLETING;
			shutdown(n->sok,SHUT_RD);

			n->events = POLLOUT;
			n->complete = 1;
			chat->state = STATE_COMPLETING;
			shutdown(n->sok,SHUT_RD);
			
			if((connected + n->time_limit) < now)
				msg = xstrdup("Timelimit Reached.\n");
			else
				msg = xstrdup("Packlist Deleted.\n");
			pqueue_push_back(&(chat->msgqueue),msg);
			UNLOCK(chat);
		}
		
		if(n->colour != CL_WHITE)
			n->colour = CL_GREEN;
		else
			n->poll = poll_add(intf,n->sok,n->events);
	} else
		UNLOCK(chat);

	return NS_NOCHANGE;
}

static void node_shuffle_by_colour(node_t *n, cl_t *cl) {
	node_t *pn;
	int i;
	
	if(cl == NULL)
		return;
	
	for(i = cl->size; i > 0; --i) {
		pn = CL_POP_FRONT(cl);
		//D("pn: %p, %d <=> %d",pn,pn->colour,cl->colour);
		if(pn->colour != cl->colour) {
			switch(pn->colour) {
				case CL_GREEN:
					CL_PUSH_BACK(n->green,pn);
					break;
				case CL_YELLOW:
					CL_PUSH_BACK(n->yellow,pn);
					break;
				case CL_RED:
					CL_PUSH_BACK(n->red,pn);
					break;
				case CL_WHITE:
					CL_PUSH_BACK(n->white,pn);
					break;
			}
		} else
			CL_PUSH_BACK(cl,pn);
	}
}

static void node_shuffle(node_t *n) {
	node_shuffle_by_colour(n,n->green);
	node_shuffle_by_colour(n,n->yellow);
	node_shuffle_by_colour(n,n->red);
	node_shuffle_by_colour(n,n->white);
}


static int interface_check_prepoll(intf_t *intf) {
	LOCK(intf->interface);
	if(intf->interface->deleted != 0)
		intf->end = 1;
	UNLOCK(intf->interface);
	
	if(intf->end == 1)
		return -1;
	
	update_bandwidth_stats(intf);
	load_interface_settings(intf);
	return 0;
}

static void setup_static_polls(intf_t *intf) {
	int i;
	if(intf->sok_unused != NULL) {
		for(i = pqueue_length(intf->sok_unused); i > 0; --i) {
			sok_t *s = pqueue_pop_front(intf->sok_unused);
			s->poll = poll_add(intf,s->sok,POLLIN);
			pqueue_push_back(intf->sok_unused,s);
			//D("i = %d, s = %p, s->port = %u",i,s,s->port);
		}
	}
}

static void run_static_polls(intf_t *intf) {
	int i,ret,sok;
	
	if(intf->sok_unused != NULL) {
		for(i = pqueue_length(intf->sok_unused); i > 0; --i) {
			sok_t *s = pqueue_pop_front(intf->sok_unused);
			if(s->poll->revents != 0) {
				do {
					ret = accept(s->sok,NULL,NULL);
					if(ret != -1) {
						sok = ret;
						close_sok(&sok);
					}
				} while(ret != -1);
			}
			pqueue_push_back(intf->sok_unused,s);
			//D("i = %d, s = %p, s->port = %u",i,s,s->port);
		}
	}
}

static void init_pool(intf_t *intf, pool_t *p); // pre-declaration
FUNC_TYPE(send_interface_check_prepoll) {
	int i;

	if(interface_check_prepoll(intf) != 0)
		return NS_DELETE;

	pool_purge(intf->interface,xtime());

	setup_static_polls(intf);
	
	LOCK(intf->interface);
	for(i = 0; i < intf->interface->send.no_pools; ++i) {
		pool_t *p = intf->interface->send.pools[i];
		//D("p: %p",p);
		LOCK(p);
		if(p->deleted == 0 && p->state == STATE_UNKNOWN)
			init_pool(intf,p);
		UNLOCK(p);
	}
	UNLOCK(intf->interface);
	
	return NS_NOCHANGE;
}

FUNC_TYPE(send_interface_check_postpoll) {
	node_shuffle(n);
	run_static_polls(intf);
	return NS_NOCHANGE;
}

static void init_recv(intf_t *intf, send_t *r); // pre-declaration
FUNC_TYPE(recv_interface_check_prepoll) {
	node_t *pn;
	int i;
	
	if(interface_check_prepoll(intf) != 0)
		return NS_DELETE;

	if(n->sok != -1)
		n->poll = poll_add(intf,n->sok,POLLIN);

	LOCK(intf->interface);
	for(i = 0; i < intf->interface->recv.no_uploads; ++i) {
		send_t *s = intf->interface->recv.uploads[i];
		LOCK(s);
		if(s->state == STATE_QUEUED) {
			D("s: %p",s);
			init_recv(intf,s);
			se_notify_recv(0,intf->interface,s,0);
		}
		UNLOCK(s);
	}
	UNLOCK(intf->interface);

	for(i = n->white->size; i > 0; --i) {
		pn = CL_POP_FRONT(n->white);
		if(pn->pos == -1) {
			calculate_resume(intf,pn);
			if(pn->pos >= pn->size) {
				pn->err = ECANCELED;
				E(pn->err);
				free_node(intf,pn);
			} else
				CL_PUSH_BACK(n->white,pn);
		} else
			CL_PUSH_BACK(n->white,pn);
	}

	return NS_NOCHANGE;
}

static void init_dccserv(intf_t *intf, int sok); // pre-declaration
FUNC_TYPE(recv_interface_check_postpoll) {
	node_shuffle(n);

	if(n->sok != -1) {
		if(n->poll->revents & POLLIN) {
			int ret, i = 0;
			do {
				ret = accept(n->sok,NULL,NULL);
				if(ret != -1) {
					init_dccserv(intf,ret);
					i++;
				}
			} while(ret != -1 && i < intf->accept_per_sec);
		}
	}

	return NS_NOCHANGE;
}

static void init_chat(intf_t *intf, chat_t *c); // pre-declaration
FUNC_TYPE(chat_interface_check_prepoll) {
	int i;

	if(interface_check_prepoll(intf) != 0)
		return NS_DELETE;
	
	setup_static_polls(intf);
	
	LOCK(intf->interface);
	for(i = 0; i < intf->interface->chat.no_chats; ++i) {
		chat_t *c = intf->interface->chat.chats[i];
		LOCK(c);
		if(c->state == STATE_QUEUED)
			init_chat(intf,c);
		UNLOCK(c);
	}
	UNLOCK(intf->interface);
	
	return NS_NOCHANGE;
}

FUNC_TYPE(chat_interface_check_postpoll) {
	node_shuffle(n);
	run_static_polls(intf);
	return NS_NOCHANGE;
}


/** Init Functions **/

static void init_send(intf_t *intf, node_t *p, send_t *s) {
	node_t *n = alloc_node();

	RLOCK(s->pack);
	n->file = xstrdup(s->pack->file);
	n->size = s->pack->size;
	RUNLOCK(s->pack);

	s->state = STATE_LISTENING;
	s->started = xtime();
	s->last_contact = s->started;
	n->state = NS_CONNECT;
	n->data = s;
	n->pool = (pool_t *)p->data;
	
	set_func(n,CHECK_PREPOLL,send_recv_check_prepoll);
	set_func(n,CHECK_POSTPOLL,dcc_check_postpoll);
	set_func(n,RUN,send_run);
	set_func(n,DELETE,send_recv_delete);
	
	switch(intf->mode) {
		case IMODE_NORMAL:
			set_func(n,CONN_PREPOLL,
					dcc_listen_prepoll);
			set_func(n,CONN_POSTPOLL,
					dcc_listen_postpoll);
			set_func(n,NEGO_PREPOLL,
					send_nego_prepoll);
			break;
		case IMODE_DCCSERVER:
			set_func(n,CONN_PREPOLL,
					dccserv_conn_prepoll);
			set_func(n,CONN_POSTPOLL,
					dcc_outbound_postpoll);
			set_func(n,NEGO_PREPOLL,
					send_chat_dccserv_nego_prepoll);
			set_func(n,NEGO_POSTPOLL,
					send_dccserv_nego_postpoll);
			break;
		case IMODE_REVERSE:
			set_func(n,CONN_PREPOLL,
					dcc_outbound_prepoll);
			set_func(n,CONN_POSTPOLL,
					dcc_outbound_postpoll);
			set_func(n,NEGO_PREPOLL,
					send_nego_prepoll);
			break;
	}
					
	CL_PUSH_BACK(p->white,n);
}

static void init_pool(intf_t *intf, pool_t *p) {
	node_t *n = alloc_node();

	//D("p: %p",p);
	
	p->state = STATE_ACTIVE;
	n->state = NS_RUN;
	n->data = p;

	set_func(n,CHECK_PREPOLL,pool_check_prepoll);
	set_func(n,CHECK_POSTPOLL,pool_check_postpoll);
	set_func(n,RUN_PREPOLL,common_run_prepoll);
	set_func(n,RUN_POSTPOLL,common_run_postpoll);
	set_func(n,RUN,colour_run);
	set_func(n,BANDWIDTH,calculate_rates);
	
	CL_PUSH_BACK(intf->root->white,n);
}

static void init_dccserv(intf_t *intf, int sok) {
	node_t *n = alloc_node();
	
	n->state	= NS_NEGOTIATE;
	n->sok		= sok;
	n->connected	= xtime();
	n->events	= POLLIN;

	log_dccserv_incoming(intf,n);
	
	set_func(n,CHECK_PREPOLL,recv_dccserv_check_prepoll);
	set_func(n,NEGO_PREPOLL,recv_dccserv_nego_prepoll);
	set_func(n,NEGO_POSTPOLL,recv_dccserv_nego_postpoll);
	set_func(n,DELETE,recv_dccserv_delete);
	
	CL_PUSH_BACK(intf->root->white,n);
}

static void recv_set_common_funcs(node_t *n) {
	set_func(n,CHECK_PREPOLL,send_recv_check_prepoll);
	set_func(n,CHECK_POSTPOLL,dcc_check_postpoll);
	set_func(n,RUN,recv_run);
	set_func(n,DELETE,send_recv_delete);
	set_func(n,NEGO_PREPOLL,recv_nego_prepoll);
}

static void init_recv(intf_t *intf, send_t *r) {
	node_t *n = alloc_node();
	dcc_aux_t *aux;
	
	RLOCK(r->pack);
	n->file = calculate_fn(intf,r->pack->file);
	n->size = r->pack->size;
	RUNLOCK(r->pack);
	
	r->state = STATE_LISTENING;
	r->started = xtime();
	r->last_contact = r->started;
	n->state = NS_CONNECT;
	n->data = r;
	aux = (dcc_aux_t *)r->aux;

	n->pos = -1;	// This is a flag to the upper level check function
			// that we need to calculate the resume.

	recv_set_common_funcs(n);
	
	if(aux->reverse == 0) {
		set_func(n,CONN_PREPOLL,dcc_outbound_prepoll);
		set_func(n,CONN_POSTPOLL,dcc_outbound_postpoll);
	} else {
		set_func(n,CONN_PREPOLL,dcc_listen_prepoll);
		set_func(n,CONN_POSTPOLL,dcc_listen_postpoll);
	}
	
	CL_PUSH_BACK(intf->root->white,n);
}

static int recv_dccserv_init_upload(intf_t *intf, node_t *n) {
	struct sockaddr_in addr;
	socklen_t len = sizeof(addr);
	dcc_aux_t *aux;
	send_t *r = NULL;
	char buffer[512],addrbuf[64];
	
	LOCK(intf->interface);
	if(intf->interface->recv.no_uploads < 
			intf->interface->recv.max_no_uploads) {
		r = alloc_send();
		LOCK(r);
		PTRARR_ADD(&(intf->interface->recv.uploads),
			&(intf->interface->recv.no_uploads),r);
	}
	UNLOCK(intf->interface);

	if(r == NULL)
		return -1;

	recv_set_common_funcs(n);
	set_func(n,NEGO_POSTPOLL,NULL);
	
	getpeername(n->sok,(struct sockaddr *)&addr,&len);
	inet_ntop(AF_INET,&(addr.sin_addr),addrbuf,sizeof(addrbuf));
	snprintf(buffer,sizeof(buffer),"%s:%u",addrbuf,ntohs(addr.sin_port));
	
	r->started	= n->connected;
	r->last_contact	= xtime();
	r->nick		= xstrdup(buffer);
	r->host		= xstrdup(addrbuf);

	pathtofn(n->file,buffer,sizeof(buffer));
	r->pack		= alloc_pack();
	r->pack->file	= xstrdup(buffer);
	r->pack->size	= n->size;
	r->pos 		= n->pos;
	
	aux 		= xalloc(sizeof(dcc_aux_t));
	r->aux		= aux;
	memcpy(&(aux->addr),&(addr.sin_addr),sizeof(struct in_addr));
	aux->port	= ntohs(addr.sin_port);
	
	se_notify_recv(0,intf->interface,r,0);
	UNLOCK(r);

	log_dccserv_upload(intf,n);
	
	n->data = r;
	
	return 0;
}

static void init_chat(intf_t *intf, chat_t *c) {
	node_t *n = alloc_node();

	D("c: %p",c);
	
	n->state = NS_CONNECT;
	n->data = c;

	c->state = STATE_LISTENING;
	c->started = xtime();
	c->last_contact = c->started;
	
	set_func(n,CHECK_PREPOLL,chat_check_prepoll);
	set_func(n,CHECK_POSTPOLL,dcc_check_postpoll);
	set_func(n,RUN,chat_run);
	set_func(n,DELETE,chat_delete);
	
	switch(intf->mode) {
		case IMODE_NORMAL:
			set_func(n,CONN_PREPOLL,
					dcc_listen_prepoll);
			set_func(n,CONN_POSTPOLL,
					dcc_listen_postpoll);
			set_func(n,NEGO_PREPOLL,
					chat_nego_prepoll);
			break;
		case IMODE_DCCSERVER:
			set_func(n,CONN_PREPOLL,
					dccserv_conn_prepoll);
			set_func(n,CONN_POSTPOLL,
					dcc_outbound_postpoll);
			set_func(n,NEGO_PREPOLL,
					send_chat_dccserv_nego_prepoll);
			set_func(n,NEGO_POSTPOLL,
					chat_dccserv_nego_postpoll);
			break;
		case IMODE_REVERSE:
			set_func(n,CONN_PREPOLL,
					dcc_outbound_prepoll);
			set_func(n,CONN_POSTPOLL,
					dcc_outbound_postpoll);
			set_func(n,NEGO_PREPOLL,
					chat_nego_prepoll);
			break;
	}

	CL_PUSH_BACK(intf->root->white,n);
}


/** Interface Running Functions **/

static uint32_t generate_tokens(node_t *n, 
		size_t token_size, uint32_t off_usec) {
	uint32_t tokens_total,tokens_used;
	int ret;
	
	if(n->c_rate == 0)
		return 16374; /* "infinite" tokens */
	if(n->p_rate >= n->c_rate)
		return 0;
	if(token_size <= 0)
		token_size = 512;

	tokens_total = ceil((float)n->c_rate / (float)token_size);
	tokens_used = ceil((float)n->p_rate / (float)token_size);
	
	if(tokens_total < 1)
		tokens_total = 1;
	else if(tokens_total > 1000000)
		tokens_total = 1000000;
	
	ret = (uint32_t)(((off_usec / (1000000 / tokens_total)) - tokens_used));
	
	ret = ret > 0 ? ret : 0;
	return ret;
}

static void interface_started(interface_t *i) {
	LOCK(i);
	LOG_TITLE("Interface %s",i->name);
	UNLOCK(i);
	LOGTP(L_INF,"Started");
}

static void interface_stopped(interface_t *i) {
	LOCK(i);
	UNLOCK(i);
	LOGTP(L_INF,"Shutdown");
}

// like diff_tv_usec, but inline
static inline uint32_t diff_usec(struct timeval *ti, struct timeval *tj) {
	if(ti->tv_sec == tj->tv_sec)
		return IMAX(ti->tv_usec,tj->tv_usec) - 
			IMIN(ti->tv_usec,tj->tv_usec);
	else {
		if(IMAX(ti->tv_sec,tj->tv_sec) == ti->tv_sec) {
			return ((ti->tv_sec - tj->tv_sec) * 1000000)
				+ (ti->tv_usec - tj->tv_usec);
		} else {
			return ((tj->tv_sec - ti->tv_sec) * 1000000)
				+ (tj->tv_usec - ti->tv_usec);
		}

	}
}

static int master_tick(intf_t *intf, uint32_t tick_length) {
	poll_wipe(intf);
	if(run_func(UPWARD,CHECK_PREPOLL,intf,intf->root) == NS_DELETE)
		return -1;
	run_func(UPWARD,CONN_PREPOLL,intf,intf->root);
	run_func(UPWARD,NEGO_PREPOLL,intf,intf->root);
	poll(intf->fds,intf->no_fds,tick_length / 1000);
	if(run_func(UPWARD,CHECK_POSTPOLL,intf,intf->root) == NS_DELETE)
		return -1;
	run_func(UPWARD,CONN_POSTPOLL,intf,intf->root);
	run_func(UPWARD,NEGO_POSTPOLL,intf,intf->root);
	run_func(DOWNWARD,BANDWIDTH,intf,intf->root);
	return 0;
}

static void minor_tick(intf_t *intf, uint32_t tick_length) {
	poll_wipe(intf);
	run_func(UPWARD,RUN_PREPOLL,intf,intf->root);
	poll(intf->fds,intf->no_fds,tick_length / 1000);
	run_func(UPWARD,RUN_POSTPOLL,intf,intf->root);

	/*
	D("n: %p, green: %d, yellow: %d, red: %d, white: %d",
		intf->root,
		intf->root->green->size,
		intf->root->yellow->size,
		intf->root->red->size,
		intf->root->white->size);
	*/
}

static void run_tokens(intf_t *intf, uint32_t tokens) {
	node_t *n = intf->root;
	if(n->c_rate == 0) {
		while((tokens--) > 0) {
			if(N_RUN(intf,n) != 1)
				break;
		}
	} else {
		while((tokens--) > 0 && n->p_rate < n->c_rate) {
			if(N_RUN(intf,n) != 1)
				break;
		}
	}
}

static void interface_run(intf_t *intf) {
	uint32_t tick_length = 20000;
	uint32_t el_usec,ticks;
	struct timeval tvs,tve;
	node_t *root;
	uint32_t tokens;

	/* run */
	while((!thread_should_end()) && intf->end == 0) {
		gettimeofday(&tvs,NULL);
		
		if(master_tick(intf,tick_length) != 0)
			break;
		root = intf->root;
		
		if(root->c_rate != 0) {
			ticks = (root->c_rate*2) / 
				IMAX(intf->snd_bufsize,intf->rcv_bufsize);
			if(ticks < intf->ticks_low)
				ticks = intf->ticks_low;
			else if(ticks > intf->ticks_high)
				ticks = intf->ticks_high;
		} else
			ticks = intf->ticks_high;
		
		tick_length = 1000000 / ticks;
		
		do {
			if(has_active(intf->root)) {
				minor_tick(intf,tick_length);
				gettimeofday(&tve,NULL);
			
				el_usec = diff_usec(&tvs,&tve);

				tokens = generate_tokens(root,
						intf->token_size,el_usec);
				if(el_usec < 1000000 && tokens == 0) {
					usleep(IMIN(tick_length,
							1000000 - el_usec));
					gettimeofday(&tve,NULL);
					el_usec = diff_usec(&tvs,&tve);
					tokens = generate_tokens(root,
						intf->token_size,
						el_usec < 1000000 ? el_usec :
						 1000000);
				}
				
				run_tokens(intf,tokens);
			} else {
				gettimeofday(&tve,NULL);
				el_usec = diff_usec(&tvs,&tve);
				if(el_usec < 1000000)
					usleep(1000000 - diff_usec(&tvs,&tve));
			}
			gettimeofday(&tve,NULL);
		} while(diff_usec(&tvs,&tve) < 1000000);
	}

	intf->end = 1;
}

void *interface_thread(void *interface_ptr) {
	interface_t	*interface = (interface_t *)interface_ptr;
	intf_t		*intf;
	
	thread_signal_started(&(interface->thread));
	
	LOCK(interface);
	intf = alloc_intf(interface->type,interface);
	UNLOCK(interface);

	load_interface_settings(intf);
	interface_started(interface);
	
	if(intf->type == INTERFACE_SEND) {
		int i,j;
		
		LOGTP(L_INF,"Using %s method.",sendfile_method);
		LOCK(interface);
		for(i = 0; i < interface->send.no_pools; ++i) {  
			pool_t *p = interface->send.pools[i];
			LOCK(p);
			for(j = 0; j < p->no_csends; ++j) {
				LOCK(p->csends[j]);
				p->csends[j]->state = STATE_QUEUED;
				UNLOCK(p->csends[j]);
			}
			UNLOCK(p);
		}
		UNLOCK(interface);
	}

	switch (intf->type) {
		case INTERFACE_SEND:
			intf->ticks_high = 64;
			set_func(intf->root,CHECK_PREPOLL,
					send_interface_check_prepoll);
			set_func(intf->root,CHECK_POSTPOLL,
					send_interface_check_postpoll);
			break;
		case INTERFACE_RECV:
			intf->ticks_high = 32;
			set_func(intf->root,CHECK_PREPOLL,
					recv_interface_check_prepoll);
			set_func(intf->root,CHECK_POSTPOLL,
					recv_interface_check_postpoll);
			break;
		case INTERFACE_CHAT:
			intf->ticks_high = 16;
			set_func(intf->root,CHECK_PREPOLL,
					chat_interface_check_prepoll);
			set_func(intf->root,CHECK_POSTPOLL,
					chat_interface_check_postpoll);
			break;
		default:
			assert(0);
			break;
	}

	intf->ticks_low = 4;
	intf->root->state = NS_RUN;
	set_func(intf->root,RUN_PREPOLL,common_run_prepoll);
	set_func(intf->root,RUN_POSTPOLL,common_run_postpoll);
	set_func(intf->root,RUN,colour_run);
	set_func(intf->root,BANDWIDTH,calculate_rates);

	interface_run(intf);
	
	free_intf(intf);
	
	interface_stopped(interface);

	thread_signal_finished();

	return NULL;
}
