/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
* -----------------------------------------------------------|   (0...0)   *
* SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
* -----------------------------------------------------------|    {o o}    *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
* Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
* Tricops and Fireblade                                      |             *
* ------------------------------------------------------------------------ *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
* Chastain, Michael Quan, and Mitchell Tse.                                *
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
* ------------------------------------------------------------------------ *
*                            Ban module by Shaddai                         *
****************************************************************************/



#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <time.h>
#include "mud.h"


/* Local functions */
void fread_ban args ( ( FILE *fp, int type ) );
bool check_expire args ( ( BAN_DATA *ban ) );
void dispose_ban args ( ( BAN_DATA *ban, int type ) );
void free_ban args ( ( BAN_DATA *pban ) );

/* Global Variables */

BAN_DATA *first_ban;
BAN_DATA *last_ban;
BAN_DATA *first_ban_class;
BAN_DATA *last_ban_class;
BAN_DATA *first_ban_race;
BAN_DATA *last_ban_race;


/*
 * Load all those nasty bans up :)
 * 	Shaddai
 */
void
load_banlist ( void )
{
	char buf[MAX_STRING_LENGTH];
	char *word;
	FILE *fp;
	bool fMatch = FALSE;

	if (!(fp = fopen (SYSTEM_DIR BAN_LIST, "r")))
	{
		bug ("Save_banlist: Cannot open " BAN_LIST, 0);
		perror (BAN_LIST);
		return ;
	}
	for ( ; ; )
	{
		word = feof (fp) ? "END" : fread_word ( fp );
		fMatch = FALSE;
		switch ( UPPER ( word[0] ) )
		{
			case 'C':
				if ( !str_cmp ( word, "CLASS" ) )
				{
					fread_ban ( fp, BAN_CLASS );
					fMatch = TRUE;
				}
				break;
			case 'E':
				if ( !str_cmp ( word, "END")) /*File should always contain END*/
				{
					fclose ( fp );
					log_string ( "Done." );
					return ;
				}
			case 'R':
				if ( !str_cmp ( word, "RACE" ) )
				{
					fread_ban ( fp, BAN_RACE );
					fMatch = TRUE;
				}
				break;
			case 'S':
				if ( !str_cmp ( word, "SITE" ) )
				{
					fread_ban ( fp, BAN_SITE );
					fMatch = TRUE;
				}
				break;
		}
		if ( !fMatch )
		{
			sprintf ( buf, "Load_banlist: no match: %s", word );
			bug ( buf, 0 );
		} /* End of switch statement */
	} /* End of for loop */
}

/*
 * Load up one class or one race ban structure.
 */

void
fread_ban ( FILE *fp, int type )
{
	BAN_DATA *pban;
	int i = 0;
	bool fMatch = FALSE;

	CREATE ( pban, BAN_DATA, 1 );

	pban->name = fread_string_nohash ( fp );
	pban->user = NULL;
	pban->level = fread_number( fp );
	pban->duration = fread_number( fp );
	pban->unban_date = fread_number( fp );
	if ( type == BAN_SITE )
	{ /* Sites have 2 extra numbers written out */
		pban->prefix = fread_number( fp );
		pban->suffix = fread_number( fp );
	}
	pban->warn = fread_number( fp );
	pban->ban_by = fread_string_nohash( fp );
	pban->ban_time = fread_string_nohash( fp );
	pban->note = fread_string( fp );

	/* Need to lookup the class or race number if it is of that type */

	if ( type == BAN_CLASS )
		for ( i = 0; i < MAX_CLASS; i++ )
		{
			if ( !str_cmp( class_table[i]->who_name, pban->name ) )
			{
				fMatch = TRUE;
				break;
			}
		}
	else if ( type == BAN_RACE )
		for ( i = 0; i < MAX_RACE; i++ )
		{
			if ( !str_cmp( race_table[i]->race_name, pban->name ) )
			{
				fMatch = TRUE;
				break;
			}
		}
	else if ( type == BAN_SITE )
		for ( i = 0; i < strlen( pban->name); i ++ )
		{
			if ( pban->name[i] == '@' )
			{
				char *temp;
				char *temp2;

				temp = str_dup( pban->name );
				temp[i] = '\0';
				temp2 = &pban->name[i + 1];
				DISPOSE( pban->name );
				pban->name = str_dup( temp2 );
				pban->user = str_dup( temp );
				DISPOSE( temp );
				break;
			}
		}

	if ( type == BAN_RACE || type == BAN_CLASS )
	{
		if ( fMatch )
			pban->flag = i;
		else /* The file is corupted throw out this ban structure */
		{
			bug("Bad class structure %d.\n\r", i );
			free_ban( pban );
			return ;
		}
	}
	if ( type == BAN_CLASS )
		LINK( pban, first_ban_class, last_ban_class, next, prev );
	else if ( type == BAN_RACE )
		LINK( pban, first_ban_race, last_ban_race, next, prev );
	else if ( type == BAN_SITE )
		LINK( pban, first_ban, last_ban, next, prev );
	else /* Bad type throw out the ban structure */
	{
		bug("Fread_ban: Bad type %d", type );
		free_ban( pban );
	}
	return ;
}

/*
 * Saves all bans, for sites, classes and races.
 * 	Shaddai
 */

void
save_banlist ( void )
{
	BAN_DATA *pban;
	FILE *fp;

	fclose (fpReserve);
	if (!(fp = fopen (SYSTEM_DIR BAN_LIST, "w")))
	{
		bug ("Save_banlist: Cannot open " BAN_LIST, 0);
		perror (BAN_LIST);
		fpReserve = fopen (NULL_FILE, "r");
		return ;
	}

	/* Print out all the site bans */

	for (pban = first_ban; pban; pban = pban->next)
	{
		fprintf (fp, "SITE\n" );
		if ( pban->user )
			fprintf ( fp, "%s@%s~\n", pban->user, pban->name );
		else
			fprintf (fp, "%s~\n", pban->name );
		fprintf (fp, "%d %d %d %d %d %d\n", pban->level, pban->duration,
		         pban->unban_date, pban->prefix, pban->suffix, pban->warn );
		fprintf (fp, "%s~\n%s~\n%s~\n", pban->ban_by, pban->ban_time,
		         pban->note );
	}

	/* Print out all the race bans */

	for (pban = first_ban_race; pban; pban = pban->next )
	{
		fprintf (fp, "RACE\n" );
		fprintf (fp, "%s~\n", pban->name );
		fprintf (fp, "%d %d %d %d\n", pban->level, pban->duration,
		         pban->unban_date, pban->warn );
		fprintf (fp, "%s~\n%s~\n%s~\n", pban->ban_by, pban->ban_time,
		         pban->note );
	}

	/* Print out all the class bans */

	for (pban = first_ban_class; pban; pban = pban->next )
	{
		fprintf (fp, "CLASS\n" );
		fprintf (fp, "%s~\n", pban->name );
		fprintf (fp, "%d %d %d %d\n", pban->level, pban->duration,
		         pban->unban_date, pban->warn );
		fprintf (fp, "%s~\n%s~\n%s~\n", pban->ban_by, pban->ban_time,
		         pban->note );
	}
	fprintf (fp, "END\n"); /* File must have an END even if empty */
	fclose (fp);
	fpReserve = fopen (NULL_FILE, "r");
	return ;
}

/*
 * The main command for ban, lots of arguments so be carefull what you
 * change here.		Shaddai
 */

void
do_ban ( CHAR_DATA *ch, char *argument )
{
	char arg1[MAX_INPUT_LENGTH];
	char arg2[MAX_INPUT_LENGTH];
	char arg3[MAX_INPUT_LENGTH];
	char arg4[MAX_INPUT_LENGTH];
	char *temp;
	BAN_DATA *pban;
	int value = 0, time;

	if (IS_NPC (ch)) /* Don't want mobs banning sites ;) */
	{
		send_to_char ("Monsters are too dumb to do that!\n\r", ch);
		return ;
	}

	if (!ch->desc) /* No desc means no go :) */
	{
		bug ("do_ban: no descriptor", 0);
		return ;
	}

	set_char_color( AT_IMMORT, ch );
	argument = one_argument( argument, arg1 );
	argument = one_argument( argument, arg2 );
	argument = one_argument( argument, arg3 );
	argument = one_argument( argument, arg4 );

	/* Do we have a time duration for the ban? */

	if ( arg4[0] != '\0' && is_number( arg4 ) )
		time = atoi( arg4 );
	else
		time = -1;


	/* -1 is default, but no reason the time should be greater than 1000
	 * or less than 1, after all if it is greater than 1000 you are talking
	 * around 3 years.
	 */

	if ( time != -1 && ( time < 1 || time > 1000 ) )
	{
		send_to_char("Time value is -1 (forever) or from 1 to 1000.\n\r", ch);
		return ;
	}

	/*
	 * Need to be carefull with sub-states or everything will get messed up.
	 */

	switch ( ch->substate )
	{
		default:
//			bug ("do_ban: illegal substate", 0);
			return ;
		case SUB_RESTRICTED:
			send_to_char ("You cannot use this command from within another command.\n\r", ch);
			return ;
		case SUB_NONE:
			ch->tempnum = SUB_NONE;
			break;

			/* Returning to end the editing of the note */

		case SUB_BAN_DESC:
			add_ban( ch, "", "", 0, 0 );
			return ;
	}
	if ( arg1[0] == '\0' )
		goto syntax_message;

	/* If no args are sent after the class/site/race, show the current banned
	 * items.  Shaddai
	 */

	if ( !str_cmp ( arg1, "site" ) )
	{
		if ( arg2[0] == '\0' )
		{
			show_bans( ch, BAN_SITE );
			return ;
		}

		/* Are they high enough to ban sites? */

		if ( get_trust(ch) < sysdata.ban_site_level )
		{
			ch_printf(ch, "You must be %d level to add bans.\n\r", sysdata.ban_site_level);
			return ;
		}
		if ( arg3[0] == '\0' )
			goto syntax_message;
		if ( !add_ban ( ch, arg2, arg3, time, BAN_SITE ) )
			return ;
	}
	else if ( !str_cmp ( arg1, "race" ) )
	{
		if ( arg2[0] == '\0' )
		{
			show_bans( ch, BAN_RACE );
			return ;
		}

		/* Are they high enough level to ban races? */

		if ( get_trust(ch) < sysdata.ban_race_level )
		{
			ch_printf(ch, "You must be %d level to add bans.\n\r", sysdata.ban_race_level);
			return ;
		}
		if ( arg3[0] == '\0' )
			goto syntax_message;
		if ( !add_ban(ch, arg2, arg3, time, BAN_RACE ) )
			return ;
	}
	else if ( !str_cmp ( arg1, "class" ) )
	{
		if ( arg2[0] == '\0' )
		{
			show_bans( ch, BAN_CLASS );
			return ;
		}

		/* Are they high enough to ban classes? */

		if ( get_trust(ch) < sysdata.ban_class_level )
		{
			ch_printf(ch, "You must be %d level to add bans.\n\r", sysdata.ban_class_level);
			return ;
		}
		if ( arg3[0] == '\0' )
			goto syntax_message;
		if ( !add_ban(ch, arg2, arg3, time, BAN_CLASS ) )
			return ;
	}
	else if ( !str_cmp ( arg1, "show" ) )
	{

		/* This will show the note attached to a ban */

		if ( arg2[0] == '\0' || arg3[0] == '\0' )
			goto syntax_message;
		temp = arg3;
		if ( arg3[0] == '#' ) /* Use #1 to show the first ban*/
		{
			temp = arg3;
			temp++;
			if ( !is_number( temp ) )
			{
				send_to_char("Which ban # to show?\n\r", ch);
				return ;
			}
			value = atoi(temp);
			if ( value < 1 )
			{
				send_to_char ("You must specify a number greater than 0.\n\r", ch);
				return ;
			}
		}
		if ( !str_cmp ( arg2, "site" ) )
		{
			pban = first_ban;
			if ( temp[0] == '*' )
				temp++;
			if ( temp[strlen(temp) - 1] == '*' )
				temp[strlen(temp) - 1] = '\0';
		}
		else if ( !str_cmp ( arg2, "class" ) )
			pban = first_ban_class;
		else if ( !str_cmp ( arg2, "race" ) )
			pban = first_ban_race;
		else
			goto syntax_message;
		for ( ; pban; pban = pban->next )
			if ( value == 1 || !str_cmp ( pban->name, temp ) )
				break;
			else if ( value > 1 )
				value--;

		if ( !pban )
		{
			send_to_char ("No such ban.\n\r", ch);
			return ;
		}
		ch_printf(ch, "Banned by: %s\n\r", pban->ban_by );
		send_to_char ( pban->note, ch );
		return ;
	}
	else
		goto syntax_message;
	return ;

	/* Catch all syntax message, make sure that return stays above this or you
	 * will get the syntax message everytime you issue the command even if it
	 * is a valid one.  Shaddai
	 */

syntax_message:
	send_to_char("Syntax: ban site  <address> <type> <duration>\n\r", ch );
	send_to_char("Syntax: ban race  <race>    <type> <duration>\n\r", ch );
	send_to_char("Syntax: ban class <class>   <type> <duration>\n\r", ch );
	send_to_char("Syntax: ban show  <field>   <number>\n\r", ch );
	send_to_char("Ban site lists current bans.\n\r", ch );
	send_to_char("Duration is the length of the ban in days.\n\r", ch );
	send_to_char("Type can be:  newbie, mortal, all, warn or level.\n\r", ch );
	send_to_char("In ban show, the <field> is site, race or class,", ch);
	send_to_char("  and the <number> is the ban number.\n\r", ch );
	return ;
}


/*
 * Allow a already banned site/class or race.  Shaddai
 */

void
do_allow ( CHAR_DATA *ch, char * argument )
{
	BAN_DATA *pban;
	char arg1[MAX_INPUT_LENGTH];
	char arg2[MAX_INPUT_LENGTH];
	char *temp = NULL;
	bool fMatch = FALSE;
	int	value = 0;

	if (IS_NPC (ch)) /* No mobs allowing sites */
	{
		send_to_char ("Monsters are too dumb to do that!\n\r", ch);
		return ;
	}

	if (!ch->desc) /* No desc is a bad thing */
	{
		bug ("do_allow: no descriptor", 0);
		return ;
	}

	argument = one_argument( argument, arg1 );
	argument = one_argument( argument, arg2 );

	set_char_color (AT_IMMORT, ch);

	if ( arg1[0] == '\0' || arg2[0] == '\0')
		goto syntax_message;
	if ( arg2[0] == '#' ) /* Use #1 to ban the first ban in the list specified */
	{
		temp = arg2;
		temp++;
		if ( !is_number( temp ) )
		{
			send_to_char("Which ban # to allow?\n\r", ch);
			return ;
		}
		value = atoi(temp);
	}
	if ( !str_cmp( arg1, "site" ) )
	{
		if ( !value )
		{
			if ( strlen(arg2) < 2 )
			{
				send_to_char("You have to have at least 2 chars for a ban\n\r", ch);
				send_to_char("If you are trying to allow by number use #\n\r", ch);
				return ;
			}

			temp = arg2;
			if ( arg2[0] == '*' )
				temp++;
			if ( temp[strlen(temp) - 1] == '*' )
				temp[ strlen(temp) - 1] = '\0';
		}

		for ( pban = first_ban; pban; pban = pban->next )
		{
			/* Need to make sure we dispose properly of the ban_data
			               * Or memory problems will be created.
			 * Shaddai
			 */

			if ( value == 1 || !str_cmp ( pban->name, temp ) )
			{
				fMatch = TRUE;
				dispose_ban( pban, BAN_SITE );
				break;
			}
			if ( value > 1 )
				value--;
		}
	}
	else if ( !str_cmp( arg1, "race" ) )
	{

		arg2[0] = toupper( arg2[0] );
		for ( pban = first_ban_race; pban; pban = pban->next )
		{
			/* Need to make sure we dispose properly of the ban_data
			               * Or memory problems will be created.
			 * Shaddai
			 */

			if ( value == 1 || !str_cmp ( pban->name, arg2 ) )
			{
				fMatch = TRUE;
				dispose_ban( pban, BAN_RACE );
				break;
			}
			if ( value > 1 )
				value--;
		}
	}
	else if ( !str_cmp( arg1, "class" ) )
	{

		arg2[0] = toupper( arg2[0] );
		for ( pban = first_ban_class; pban; pban = pban->next )
		{
			/* Need to make sure we dispose properly of the ban_data
			               * Or memory problems will be created.
			 * Shaddai
			 */

			if ( value == 1 || !str_cmp ( pban->name, arg2 ) )
			{
				fMatch = TRUE;
				dispose_ban( pban, BAN_CLASS );
				break;
			}
			if ( value > 1 )
				value--;
		}
	}
	else
		goto syntax_message;

	if ( fMatch )
	{
		save_banlist();
		ch_printf(ch, "%s is now allowed.\n\r", arg2 );
	}
	else
		ch_printf(ch, "%s was not banned.\n\r", arg2 );
	return ;

	/*
	 *  Make sure that return above stays in!
	 */

syntax_message:
	send_to_char ("Syntax: allow site  <address>\n\r", ch );
	send_to_char ("Syntax: allow race  <race>\n\r", ch );
	send_to_char ("Syntax: allow class <class>\n\r", ch );
	return ;
}

/*
 *  Sets the warn flag on bans.
 */
void
do_warn ( CHAR_DATA *ch, char *argument )
{
	char arg1[MAX_STRING_LENGTH];
	char arg2[MAX_STRING_LENGTH];
	char *name;
	int count = -1, type;
	BAN_DATA *pban, *start, *end;

	/*
	 * Don't want mobs or link-deads doing this.
	 */

	if (IS_NPC (ch))
	{
		send_to_char ("Monsters are too dumb to do that!\n\r", ch);
		return ;
	}

	if (!ch->desc)
	{
		bug ("do_warn: no descriptor", 0);
		return ;
	}

	argument = one_argument( argument, arg1 );
	argument = one_argument( argument, arg2 );

	if ( arg1[0] == '\0' || arg2[0] == '\0' )
		goto syntax_message;

	if ( arg2[0] == '#' )
	{
		name = arg2;
		name++;
		if ( !is_number( name ) )
			goto syntax_message;
		count = atoi( name );
		if ( count < 1 )
		{
			send_to_char("The number has to be above 0.\n\r", ch );
			return ;
		}
	}

	/*
	 *  We simply set up which ban list we will be looking at here.
	 */
	if ( !str_cmp( arg1, "class") )
		type = BAN_CLASS;
	else if ( !str_cmp( arg1, "race" ) )
		type = BAN_RACE;
	else if ( !str_cmp( arg1, "site" ) )
		type = BAN_SITE;
	else
		type = -1;


	if ( type == BAN_CLASS )
	{
		pban = first_ban_class;
		start = first_ban_class;
		end = last_ban_class;
		arg2[0] = toupper( arg2[0] );
	}
	else if ( type == BAN_RACE )
	{
		pban = first_ban_race;
		start = first_ban_race;
		end = last_ban_race;
		arg2[0] = toupper( arg2[0] );
	}
	else if ( type == BAN_SITE )
	{
		pban = first_ban;
		start = first_ban;
		end = last_ban;
	}
	else
		goto syntax_message;
	for ( ; pban && count != 0; count--, pban = pban->next )
		if ( count == -1 && !str_cmp(pban->name, arg2 ) )
			break;
	if ( pban )
	{
		/* If it is just a warn delete it, otherwise remove the warn flag. */

		if ( pban->warn )
		{
			if ( pban->level == BAN_WARN )
			{
				dispose_ban( pban, type );
				send_to_char("Warn has been deleted.\n\r", ch );
			}
			else
			{
				pban->warn = FALSE;
				send_to_char("Warn turned off.\n\r", ch);
			}
		}
		else
		{
			pban->warn = TRUE;
			send_to_char("Warn turned on.\n\r", ch );
		}
		save_banlist( );
	}
	else
	{
		ch_printf(ch, "%s was not found in the ban list.\n\r", arg2 );
		return ;
	}
	return ;
	/* The above return has to stay in! */
syntax_message:
	send_to_char ("Syntax: warn class <field>\n\r", ch );
	send_to_char ("Syntax: warn race  <field>\n\r", ch );
	send_to_char ("Syntax: warn site  <field>\n\r", ch );
	send_to_char ("Field is either #(ban_number) or the site/class/race.\n\r", ch);
	send_to_char ("Example:  warn class #1\n\r", ch );
	return ;
}

/*
 *  This actually puts the new ban into the proper linked list and
 *  initializes its data.  Shaddai
 */

int
add_ban( CHAR_DATA *ch, char *arg1, char *arg2, int time, int type )
{
	char arg[MAX_STRING_LENGTH];
	char buf[MAX_STRING_LENGTH];
	BAN_DATA *pban, *temp;
	struct tm *tms;
	char *name;
	int level, i, value;

	/* Should we check to see if they have dropped link sometime in between
	 * writing the note and now?  Not sure but for right now we won't since
	 * do_ban checks for that.  Shaddai
	 */

	switch ( ch->substate )
	{
		default:
//			bug ("add_ban: illegal substate", 0);
			return 0;
		case SUB_RESTRICTED:
			send_to_char ("You cannot use this command from within another command.\n\r", ch);
			return 0;
		case SUB_NONE:
			{
				one_argument( arg1, arg );
				smash_tilde ( arg ); /* Make sure the immortals don't put a ~ in it. */

				if ( arg[0] == '\0' || arg2[0] == '\0')
					return 0;

				if ( is_number( arg2 ) )
				{
					level = atoi ( arg2 );
					if ( level < 0 || level > LEVEL_SUPREME )
					{
						ch_printf(ch, "Level range is from 0 to %d.\n\r", LEVEL_SUPREME );
						return 0;
					}
				}
				else if ( !str_cmp( arg2, "all" ) )
					level = LEVEL_SUPREME;
				else if ( !str_cmp( arg2, "newbie" ) )
					level = 1;
				else if ( !str_cmp( arg2, "mortal" ) )
					level = LEVEL_AVATAR;
				else if ( !str_cmp( arg2, "warn" ) )
					level = BAN_WARN;
				else
				{
					bug("Bad string for flag in add_ban.", 0 );
					return 0;
				}

				switch ( type )
				{
					case BAN_CLASS:
						if ( arg[0] == '\0' )
							return 0;
						if ( is_number( arg ) )
							value = atoi( arg );
						else
						{
							for ( i = 0; i < MAX_CLASS; i++ )
								if ( !str_cmp( class_table[i]->who_name, arg ) )
									break;
							value = i;
						}
						if (value < 0 || value >= MAX_CLASS)
						{
							send_to_char("Unknown class.\n\r", ch );
							return 0;
						}
						for ( temp = first_ban_class; temp; temp = temp->next )
						{
							if ( temp->flag == value )
							{
								if ( temp->level == level )
								{
									send_to_char ("That entry already exists.\n\r", ch);
									return 0;
								}
								else
								{
									temp->level = level;
									if ( temp->level == BAN_WARN )
										temp->warn = TRUE;
									sprintf (buf, "%24.24s", ctime (&current_time));
									temp->ban_time = str_dup (buf);
									if ( temp->ban_by )
										DISPOSE( temp->ban_by );
									temp->ban_by = str_dup ( ch->name );
									send_to_char("Updated entry.\n\r", ch);
									return 1;
								}
							}
						}
						CREATE( pban, BAN_DATA, 1 );
						pban->name = str_dup ( class_table[value]->who_name );
						pban->flag = value;
						pban->level = level;
						pban->ban_by = str_dup ( ch->name );
						LINK( pban, first_ban_class, last_ban_class, next, prev );
						break;
					case BAN_RACE:
						if ( is_number( arg ) )
							value = atoi( arg );
						else
						{
							for ( i = 0; i < MAX_RACE; i++ )
								if ( !str_cmp(race_table[i]->race_name, arg) )
									break;
							value = i;
						}
						if (value < 0 || value >= MAX_RACE)
						{
							send_to_char("Unknown race.\n\r", ch );
							return 0;
						}
						for ( temp = first_ban_race; temp; temp = temp->next )
						{
							if ( temp->flag == value )
							{
								if ( temp->level == level )
								{
									send_to_char ("That entry already exists.\n\r", ch);
									return 0;
								}
								else
								{
									temp->level = level;
									if ( temp->level == BAN_WARN )
										temp->warn = TRUE;
									sprintf (buf, "%24.24s", ctime (&current_time));
									temp->ban_time = str_dup (buf);
									if ( temp->ban_by )
										DISPOSE( temp->ban_by );
									temp->ban_by = str_dup ( ch->name );
									send_to_char("Updated entry.\n\r", ch);
									return 1;
								}
							}
						}
						CREATE( pban, BAN_DATA, 1 );
						pban->name = str_dup ( race_table[value]->race_name );
						pban->flag = value;
						pban->level = level;
						pban->ban_by = str_dup ( ch->name );
						LINK( pban, first_ban_race, last_ban_race, next, prev );
						break;
					case BAN_SITE:
						{
							bool prefix = FALSE, suffix = FALSE, user_name = FALSE;
							char *temp_host = NULL, *temp_user = NULL;
							int x;

							for ( x = 0; x < strlen(arg); x++ )
							{
								if ( arg[x] == '@' )
								{
									user_name = TRUE;
									temp_host = str_dup( &arg[x + 1]);
									arg[x] = '\0';
									temp_user = str_dup( arg );
									break;
								}
							}
							if ( !user_name )
								name = arg;
							else
								name = temp_host;
							if ( name[0] == '*' )
							{
								prefix = TRUE;
								name++;
							}

							if ( name[strlen(name) - 1] == '*' )
							{
								suffix = TRUE;
								name[strlen(name) - 1] = '\0';
							}
							for ( temp = first_ban; temp; temp = temp->next )
							{
								if ( !str_cmp ( temp->name, name ) )
								{
									if ( temp->level == level && (prefix && temp->prefix)
									     && ( suffix && temp->suffix ) && (!user_name ||
									                                       (user_name && !str_cmp(temp->user, temp_user))))
									{
										send_to_char ("That entry already exists.\n\r", ch);
										return 0;
									}
									else
									{
										temp->suffix = suffix;
										temp->prefix = prefix;
										if ( temp->level == BAN_WARN )
											temp->warn = TRUE;
										temp->level = level;
										sprintf (buf, "%24.24s", ctime (&current_time));
										temp->ban_time = str_dup (buf);
										if ( temp->ban_by )
											DISPOSE( temp->ban_by );
										if ( user_name )
										{
											DISPOSE( temp_host );
											DISPOSE( temp_user );
										}
										temp->ban_by = str_dup ( ch->name );
										send_to_char("Updated entry.\n\r", ch);
										return 1;
									}
								}
							}
							CREATE( pban, BAN_DATA, 1 );
							pban->ban_by = str_dup ( ch->name );
							pban->suffix = suffix;
							pban->prefix = prefix;
							pban->name = str_dup( name );
							pban->level = level;
							if ( user_name )
							{
								pban->user = str_dup( temp_user );
								DISPOSE( temp_host );
								DISPOSE( temp_user );
							}
							LINK( pban, first_ban, last_ban, next, prev );
							break;
						}
					default:
						bug("Bad type in add_ban: %d.", type );
						return 0;
				}
				sprintf (buf, "%24.24s", ctime (&current_time));
				pban->ban_time = str_dup (buf);
				if ( time > 0 )
				{
					pban->duration = time;
					tms = localtime(&current_time);
					tms->tm_mday += time;
					pban->unban_date = mktime(tms);
				}
				else
				{
					pban->duration = -1;
					pban->unban_date = -1;
				}
				if ( pban->level == BAN_WARN )
					pban->warn = TRUE;
				ch->substate = SUB_BAN_DESC;
				ch->dest_buf = pban;
				if ( !pban->note )
					pban->note = STRALLOC ("");
				;
				start_editing( ch, pban->note );
				return 1;
			}
		case SUB_BAN_DESC:
			pban = ch->dest_buf;
			if ( !pban )
			{
				bug ("do_ban: sub_ban_desc: NULL ch->dest_buf", 0);
				ch->substate = SUB_NONE;
				return 0;
			}
			if ( pban->note )
				STRFREE( pban->note );
			pban->note = copy_buffer( ch );
			stop_editing( ch );
			ch->substate = ch->tempnum;
			save_banlist( );
			if ( pban->duration > 0 )
			{
				if ( !pban->user )
					ch_printf (ch, "%s banned for %d days.\n\r", pban->name, pban->duration );
				else
					ch_printf (ch, "%s@%s banned for %d days.\n\r", pban->user, pban->name, pban->duration );
			}
			else
			{
				if ( !pban->user )
					ch_printf (ch, "%s banned forever.\n\r", pban->name );
				else
					ch_printf (ch, "%s@%s banned forever.\n\r", pban->user, pban->name );
			}
			return 1;
	}
	return 1;
}

/*
 * Print the bans out to the screen.  Shaddai
 */

void
show_bans ( CHAR_DATA *ch, int type )
{
	BAN_DATA *pban;
	int bnum;

	set_pager_color (AT_IMMORT, ch);

	switch ( type )
	{
		case BAN_SITE:
			send_to_pager("Banned sites:\n\r", ch);
			send_to_pager("[ #] Warn (Lv) Time                     By              For   Site\n\r", ch);
			send_to_pager("---- ---- ---- ------------------------ --------------- ----  ---------------\n\r", ch);
			pban = first_ban;
			set_pager_color( AT_PLAIN, ch );
			for ( bnum = 1; pban; pban = pban->next, bnum++)
			{
				if ( !pban->user )
					pager_printf (ch, "[%2d] %-4s (%2d) %-24s %-15s %4d  %c%s%c\n\r",
					              bnum, (pban->warn) ? "YES" : "no", pban->level,
					              pban->ban_time, pban->ban_by, pban->duration,
					              (pban->prefix) ? '*' : ' ',
					              pban->name, (pban->suffix) ? '*' : ' ');
				else
					pager_printf (ch, "[%2d] %-4s (%2d) %-24s %-15s %4d  %s@%c%s%c\n\r",
					              bnum, (pban->warn) ? "YES" : "no", pban->level,
					              pban->ban_time, pban->ban_by, pban->duration,
					              pban->user,
					              (pban->prefix) ? '*' : ' ',
					              pban->name, (pban->suffix) ? '*' : ' ');
			}
			return ;
		case BAN_RACE:
			send_to_pager("Banned races:\n\r", ch);
			send_to_pager("[ #] Warn (Lv) Time                     By              For   Race\n\r", ch);
			pban = first_ban_race;
			break;
		case BAN_CLASS:
			send_to_pager("Banned classes:\n\r", ch);
			send_to_pager("[ #] Warn (Lv) Time                     By              For   Class\n\r", ch);
			pban = first_ban_class;
			break;
		default:
			bug ("Bad type in show_bans: %d", type );
			return ;
	}
	send_to_pager("---- ---- ---- ------------------------ --------------- ----  ---------------\n\r", ch);
	set_pager_color( AT_PLAIN, ch );
	for ( bnum = 1; pban; pban = pban->next, bnum++)
		pager_printf (ch, "[%2d] %-4s (%2d) %-24s %-15s %4d  %s\n\r", bnum,
		              (pban->warn) ? "YES" : "no", pban->level, pban->ban_time, pban->ban_by,
		              pban->duration, pban->name);
	return ;
}

/*
 * Check for totally banned sites.  Need this because we don't have a
 * char struct yet.  Shaddai
 */

bool
check_total_bans ( DESCRIPTOR_DATA *d )
{
	BAN_DATA *pban;
	char new_host[MAX_STRING_LENGTH];
	int i;

	for ( i = 0; i < (int) strlen( d->host ) ; i++ )
		new_host[i] = LOWER( d->host[i] );
	new_host[i] = '\0';

	for ( pban = first_ban; pban; pban = pban->next )
	{
		if ( pban->level != LEVEL_SUPREME )
			continue;
		if ( pban->user && str_cmp( d->user, pban->user ) )
			continue;
		if ( pban->prefix && pban->suffix &&
		     strstr( pban->name, new_host ) )
		{
			if ( check_expire( pban ) )
			{
				dispose_ban( pban, BAN_SITE );
				save_banlist( );
				return FALSE;
			}
			else
				return TRUE;
		}
		/*
		 *   Bug of switched checks noticed by Cronel
		 */
		if ( pban->suffix && !str_prefix( pban->name, new_host ) )
		{
			if ( check_expire( pban ) )
			{
				dispose_ban( pban, BAN_SITE );
				save_banlist( );
				return FALSE;
			}
			else
				return TRUE;
		}
		if ( pban->prefix && !str_suffix( pban->name, new_host ) )
		{
			if ( check_expire( pban ) )
			{
				dispose_ban( pban, BAN_SITE );
				save_banlist( );
				return FALSE;
			}
			else
				return TRUE;
		}
		if ( !str_cmp( pban->name, new_host ) )
		{
			if ( check_expire( pban ) )
			{
				dispose_ban( pban, BAN_SITE );
				save_banlist( );
				return FALSE;
			}
			else
				return TRUE;
		}
	}
	return FALSE;
}

/*
 * The workhose, checks for bans on sites/classes and races. Shaddai
 */

bool
check_bans( CHAR_DATA *ch, int type )
{
	char buf[MAX_STRING_LENGTH];
	BAN_DATA *pban;
	char new_host[MAX_STRING_LENGTH];
	int i;
	bool fMatch = FALSE;

	switch ( type )
	{
		case BAN_RACE:
			pban = first_ban_race;
			break;
		case BAN_CLASS:
			pban = first_ban_class;
			break;
		case BAN_SITE:
			pban = first_ban;
			for ( i = 0; i < (int) ( strlen( ch->desc->host ) ); i++ )
				new_host[i] = LOWER( ch->desc->host[i] );
			new_host[i] = '\0';
			break;
		default:
			bug ( "Ban type in check_bans: %d.", type );
			return FALSE;
	}
	for ( ; pban; pban = pban->next )
	{
		if ( type == BAN_CLASS && pban->flag == ch->class )
		{
			if ( check_expire( pban ) )
			{
				dispose_ban( pban, BAN_CLASS );
				save_banlist( );
				return FALSE;
			}
			if ( ch->level > pban->level )
			{
				if ( pban->warn )
				{
					sprintf( buf, "%s class logging in from %s.",
					         pban->name, ch->desc->host );
					log_string_plus( buf, LOG_WARN, sysdata.log_level );
				}
				return FALSE;
			}
			else
				return TRUE;
		}
		if ( type == BAN_RACE && pban->flag == ch->race )
		{
			if ( check_expire( pban ) )
			{
				dispose_ban( pban, BAN_RACE );
				save_banlist( );
				return FALSE;
			}
			if ( ch->level > pban->level )
			{
				if ( pban->warn )
				{
					sprintf( buf, "%s race logging in from %s.",
					         pban->name, ch->desc->host );
					log_string_plus( buf, LOG_WARN, sysdata.log_level );
				}
				return FALSE;
			}
			else
				return TRUE;
		}
		if ( type == BAN_SITE )
		{
			if ( pban->prefix && pban->suffix &&
			     strstr( pban->name, new_host ) )
				fMatch = TRUE;
			else if ( pban->prefix && !str_suffix( pban->name, new_host ) )
				fMatch = TRUE;
			else if ( pban->suffix && !str_prefix( pban->name, new_host ) )
				fMatch = TRUE;
			else if ( !str_cmp( pban->name, new_host ) )
				fMatch = TRUE;
			if ( fMatch && pban->user &&
			     str_cmp(pban->user, ch->desc->user) )
				fMatch = FALSE;
			if ( fMatch )
			{
				if ( check_expire( pban ) )
				{
					dispose_ban( pban, BAN_SITE );
					save_banlist( );
					return FALSE;
				}
				if ( ch->level > pban->level )
				{
					if ( pban->warn )
					{
						sprintf( buf, "%s logging in from site %s.",
						         ch->name, ch->desc->host );
						log_string_plus( buf, LOG_WARN, sysdata.log_level );
					}
					return FALSE;
				}
				else
					return TRUE;
			}
		}
	}
	return FALSE;
}

bool check_expire( BAN_DATA *pban )
{
	char buf[MAX_STRING_LENGTH];

	if ( pban->unban_date < 0 )
		return FALSE;
	if ( pban->unban_date <= current_time )
	{
		sprintf( buf, "%s ban has expired.", pban->name );
		log_string_plus( buf, LOG_WARN, sysdata.log_level );
		return TRUE;
	}
	return FALSE;
}

void dispose_ban ( BAN_DATA *pban , int type)
{
	if ( !pban )
		return ;

	if ( type != BAN_SITE && type != BAN_CLASS && type != BAN_RACE )
	{
		bug("Dispose_ban: Unknown Ban Type %d.", type );
		return ;
	}

	switch ( type )
	{
		case BAN_SITE:
			UNLINK(pban, first_ban, last_ban, next, prev);
			break;
		case BAN_CLASS:
			UNLINK(pban, first_ban_class, last_ban_class, next,
			       prev );
			break;
		case BAN_RACE:
			UNLINK(pban, first_ban_race, last_ban_race, next,
			       prev);
			break;
	}
	free_ban( pban );
	return ;
}

void free_ban( BAN_DATA *pban )
{
	if ( pban->name )
		DISPOSE( pban->name );
	if ( pban->ban_time )
		DISPOSE( pban->ban_time );
	if ( pban->note )
		STRFREE( pban->note );
	if ( pban->user )
		DISPOSE( pban->user );
	if ( pban->ban_by )
		DISPOSE( pban->ban_by );
	if ( pban->ban_time )
		DISPOSE( pban->ban_time );
	DISPOSE( pban );
}

