/*
 *  ircd-ratbox: A slightly useful ircd.
 *  class.c: Controls connection classes.
 *
 *  Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
 *  Copyright (C) 1996-2002 Hybrid Development Team
 *  Copyright (C) 2002-2005 ircd-ratbox development team
 *
 *  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
 */

#include "stdinc.h"
#include "defaults.h"

#include "class.h"
#include "client.h"
#include "ircd.h"
#include "numeric.h"
#include "s_conf.h"
#include "s_newconf.h"
#include "send.h"
#include "match.h"

#define BAD_PING                -2

rb_dlink_list class_list;
struct Class *default_class;

struct Class *
make_class(void)
{
	struct Class *tmp;

	tmp = rb_malloc(sizeof(struct Class));

	ConFreq(tmp) = DEFAULT_CONNECTFREQUENCY;
	PingFreq(tmp) = DEFAULT_PINGFREQUENCY;
	MaxUsers(tmp) = 1;
	MaxSendq(tmp) = DEFAULT_SENDQ;

	tmp->ip_limits = rb_new_patricia(PATRICIA_BITS);
	return tmp;
}

void
free_class(struct Class *tmp)
{
	if(tmp->ip_limits)
		rb_destroy_patricia(tmp->ip_limits, NULL);

	rb_free(tmp->class_name);
	rb_free(tmp);

}

/*
 * get_conf_ping
 *
 * inputs	- pointer to struct ConfItem
 * output	- ping frequency
 * side effects - NONE
 */
static int
get_conf_ping(struct ConfItem *aconf)
{
	if((aconf) && ClassPtr(aconf))
		return (ConfPingFreq(aconf));

	return (BAD_PING);
}

/*
 * get_client_class
 *
 * inputs	- pointer to client struct
 * output	- pointer to name of class
 * side effects - NONE
 */
const char *
get_client_class(struct Client *target_p)
{
	const char *retc = "unknown";

	if(target_p == NULL || IsMe(target_p))
		return retc;

	if(IsServer(target_p))
	{
		struct server_conf *server_p = target_p->localClient->att_sconf;
		return server_p->class_name;
	}
	else
	{
		struct ConfItem *aconf;
		aconf = target_p->localClient->att_conf;

		if((aconf == NULL) || (aconf->className == NULL))
			retc = "default";
		else
			retc = aconf->className;
	}

	return (retc);
}

/*
 * get_client_ping
 *
 * inputs	- pointer to client struct
 * output	- ping frequency
 * side effects - NONE
 */
int
get_client_ping(struct Client *target_p)
{
	int ping = 0;

	if(IsServer(target_p))
	{
		struct server_conf *server_p = target_p->localClient->att_sconf;
		ping = PingFreq(server_p->class);
	}
	else
	{
		struct ConfItem *aconf;

		aconf = target_p->localClient->att_conf;

		if(aconf != NULL)
			ping = get_conf_ping(aconf);
		else
			ping = DEFAULT_PINGFREQUENCY;
	}

	if(ping <= 0)
		ping = DEFAULT_PINGFREQUENCY;

	return ping;
}

/*
 * get_con_freq
 *
 * inputs	- pointer to class struct
 * output	- connection frequency
 * side effects - NONE
 */
int
get_con_freq(struct Class *clptr)
{
	if(clptr)
		return (ConFreq(clptr));
	return (DEFAULT_CONNECTFREQUENCY);
}

/* add_class()
 *
 * input	- class to add
 * output	-
 * side effects - class is added to class_list if new, else old class
 *                is updated with new values.
 */
void
add_class(struct Class *classptr)
{
	struct Class *tmpptr;

	tmpptr = find_class(classptr->class_name);

	if(tmpptr == default_class)
	{
		rb_dlinkAddAlloc(classptr, &class_list);
		CurrUsers(classptr) = 0;
	}
	else
	{
		MaxUsers(tmpptr) = MaxUsers(classptr);
		MaxLocal(tmpptr) = MaxLocal(classptr);
		MaxGlobal(tmpptr) = MaxGlobal(classptr);
		MaxIdent(tmpptr) = MaxIdent(classptr);
		PingFreq(tmpptr) = PingFreq(classptr);
		MaxSendq(tmpptr) = MaxSendq(classptr);
		ConFreq(tmpptr) = ConFreq(classptr);
		CidrIpv4Bitlen(tmpptr) = CidrIpv4Bitlen(classptr);
		CidrIpv6Bitlen(tmpptr) = CidrIpv6Bitlen(classptr);
		CidrAmount(tmpptr) = CidrAmount(classptr);

		free_class(classptr);
	}
}


/*
 * find_class
 *
 * inputs	- string name of class
 * output	- corresponding class pointer
 * side effects	- NONE
 */
struct Class *
find_class(const char *classname)
{
	struct Class *cltmp;
	rb_dlink_node *ptr;

	if(classname == NULL)
		return default_class;

	RB_DLINK_FOREACH(ptr, class_list.head)
	{
		cltmp = ptr->data;

		if(!strcmp(ClassName(cltmp), classname))
			return cltmp;
	}

	return default_class;
}

/*
 * check_class
 *
 * inputs	- NONE
 * output	- NONE
 * side effects	-
 */
void
check_class()
{
	struct Class *cltmp;
	rb_dlink_node *ptr;
	rb_dlink_node *next_ptr;

	RB_DLINK_FOREACH_SAFE(ptr, next_ptr, class_list.head)
	{
		cltmp = ptr->data;

		if(MaxUsers(cltmp) < 0)
		{
			rb_dlinkDestroy(ptr, &class_list);
			if(CurrUsers(cltmp) <= 0)
				free_class(cltmp);
		}
	}
}

/*
 * initclass
 *
 * inputs	- NONE
 * output	- NONE
 * side effects	-
 */
void
initclass()
{
	default_class = make_class();
	ClassName(default_class) = rb_strdup("default");
}

/*
 * report_classes
 *
 * inputs	- pointer to client to report to
 * output	- NONE
 * side effects	- class report is done to this client
 */
void
report_classes(struct Client *source_p)
{
	struct Class *cltmp;
	rb_dlink_node *ptr;

	RB_DLINK_FOREACH(ptr, class_list.head)
	{
		cltmp = ptr->data;

		sendto_one_numeric(source_p, RPL_STATSYLINE,
				form_str(RPL_STATSYLINE),
				ClassName(cltmp), PingFreq(cltmp),
				ConFreq(cltmp), MaxUsers(cltmp),
				MaxSendq(cltmp),
				MaxLocal(cltmp), MaxIdent(cltmp),
				MaxGlobal(cltmp), MaxIdent(cltmp),
				CurrUsers(cltmp));
	}

	/* also output the default class */
	sendto_one_numeric(source_p, RPL_STATSYLINE, form_str(RPL_STATSYLINE),
			ClassName(default_class), PingFreq(default_class),
			ConFreq(default_class), MaxUsers(default_class),
			MaxSendq(default_class),
			MaxLocal(default_class), MaxIdent(default_class),
			MaxGlobal(default_class), MaxIdent(default_class),
			CurrUsers(default_class));
}

/*
 * get_sendq
 *
 * inputs	- pointer to client
 * output	- sendq for this client as found from its class
 * side effects	- NONE
 */
long
get_sendq(struct Client *client_p)
{
	if(client_p == NULL || IsMe(client_p))
		return DEFAULT_SENDQ;

	if(IsServer(client_p))
	{
		struct server_conf *server_p;
		server_p = client_p->localClient->att_sconf;
		return MaxSendq(server_p->class);
	}
	else
	{
		struct ConfItem *aconf = client_p->localClient->att_conf;

		if(aconf != NULL && aconf->status & CONF_CLIENT)
			return ConfMaxSendq(aconf);
	}

	return DEFAULT_SENDQ;
}
