LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/color/
LOP/councils/
LOP/deity/
LOP/src/specials/
/*****************************************************************************
 * DikuMUD (C) 1990, 1991 by:                                                *
 *   Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen,   *
 *   and Katja Nyboe.                                                        *
 *---------------------------------------------------------------------------*
 * MERC 2.1 (C) 1992, 1993 by:                                               *
 *   Michael Chastain, Michael Quan, and Mitchell Tse.                       *
 *---------------------------------------------------------------------------*
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider.                    *
 *   Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
 *         gorog, Grishnakh, Nivek, Tricops, and Fireblade.                  *
 *---------------------------------------------------------------------------*
 * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community.              *
 *                    Their contributions are greatly appreciated.           *
 *---------------------------------------------------------------------------*
 * LoP (C) 2006, 2007, 2008, 2009 by: the LoP team.                          *
 *---------------------------------------------------------------------------*
 *                            Ban module by Shaddai                          *
 *****************************************************************************/

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

/* Local functions */
bool check_expire( BAN_DATA *ban );
void dispose_ban( BAN_DATA *ban, int type );
int add_ban( CHAR_DATA *ch, char *arg1, char *arg2, int btime, int type );
void save_banlist( void );

/* Global Variables */
BAN_DATA *first_ban, *last_ban;
BAN_DATA *first_ban_class, *last_ban_class;
BAN_DATA *first_ban_race, *last_ban_race;

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

/* Print the bans out to the screen.  Shaddai */
void show_bans( CHAR_DATA *ch, int type )
{
   BAN_DATA *pban;
   int bnum = 0;

   set_pager_color( AT_IMMORT, ch );
   switch( type )
   {
      case BAN_SITE:
         send_to_pager( "Banned sites:\r\n", ch );
         send_to_pager( "[ #] Warn (Lv) Time                     By              For   Site\r\n", ch );
         send_to_pager( "---- ---- ---- ------------------------ --------------- ----  ---------------\r\n", ch );
         set_pager_color( AT_PLAIN, ch );
         for( pban = first_ban; pban; pban = pban->next )
         {
            pager_printf( ch, "[%2d] %-4s (%2d) %-24s %-15s %4d  %c%s%c\r\n",
                ++bnum, ( pban->warn ) ? "YES" : "no", pban->level,
                pban->ban_time, pban->ban_by, pban->duration,
                ( pban->prefix ) ? '*' : ' ', pban->name, ( pban->suffix ) ? '*' : ' ' );
         }
         return;

      case BAN_RACE:
         send_to_pager( "Banned races:\r\n", ch );
         send_to_pager( "[ #] Warn (Lv) Time                     By              For   Race\r\n", ch );
         pban = first_ban_race;
         break;

      case BAN_CLASS:
         send_to_pager( "Banned classes:\r\n", ch );
         send_to_pager( "[ #] Warn (Lv) Time                     By              For   Class\r\n", ch );
         pban = first_ban_class;
         break;

      default:
         bug( "%s: Bad type %d", __FUNCTION__, type );
         return;
   }
   send_to_pager( "---- ---- ---- ------------------------ --------------- ----  ---------------\r\n", ch );
   set_pager_color( AT_PLAIN, ch );
   for( ; pban; pban = pban->next )
      pager_printf( ch, "[%2d] %-4s (%2d) %-24s %-15s %4d  %s\r\n", ++bnum,
         ( pban->warn ) ? "YES" : "no", pban->level, pban->ban_time, pban->ban_by, pban->duration, pban->name );
}

/* The workhose, checks for bans on sites/classes and races. Shaddai */
bool check_bans( CHAR_DATA *ch, int type )
{
   BAN_DATA *pban;
   char new_host[MSL];
   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( "%s Ban type %d.", __FUNCTION__, type );
         return false;
   }
   for( ; pban; pban = pban->next )
   {
      if( type == BAN_CLASS )
      {
         MCLASS_DATA *mclass;
         bool banned = false;

         for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next )
         {
            if( pban->flag == mclass->wclass )
            {
               banned = true;
               break;
            }
         }
         if( !banned )
            return false;
         if( check_expire( pban ) )
         {
            dispose_ban( pban, BAN_CLASS );
            save_banlist( );
            return false;
         }
         if( ch->level > pban->level )
         {
            if( pban->warn )
               log_printf_plus( LOG_WARN, sysdata.perm_log, "%s class logging in from %s.", pban->name, ch->desc->host );
            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 )
               log_printf_plus( LOG_WARN, sysdata.perm_log, "%s race logging in from %s.", pban->name, ch->desc->host );
            return false;
         }
         else
            return true;
      }
      if( type == BAN_SITE )
      {
         if( pban->prefix && pban->suffix && strstr( new_host, pban->name ) )
            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 )
         {
            if( check_expire( pban ) )
            {
               dispose_ban( pban, BAN_SITE );
               save_banlist( );
               return false;
            }
            if( ch->level > pban->level )
            {
               if( pban->warn )
                  log_printf_plus( LOG_WARN, sysdata.perm_log, "%s logging in from site %s.", ch->name, ch->desc->host );
               return false;
            }
            else
               return true;
         }
      }
   }
   return false;
}

/* Load up one class or one race ban structure. */
void fread_ban( FILE *fp, int type )
{
   BAN_DATA *pban;
   unsigned int i = 0;
   bool fMatch = false;

   CREATE( pban, BAN_DATA, 1 );
   pban->name = fread_string( fp );
   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( fp );
   pban->ban_time = fread_string( 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]->name, pban->name ) )
         {
            fMatch = true;
            break;
         }
      }
   }
   else if( type == BAN_RACE )
   {
      for( i = 0; i < MAX_RACE; i++ )
      {
         if( !str_cmp( race_table[i]->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 = STRALLOC( pban->name );
            temp[i] = '\0';
            temp2 = &pban->name[i + 1];
            STRSET( pban->name, temp2 );
            STRFREE( 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( "%s: Bad class or race structure %d.", __FUNCTION__, 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( "%s: Bad type %d", __FUNCTION__, type );
      free_ban( pban );
   }
}

/* Load all those nasty bans up :) Shaddai */
void load_banlist( void )
{
   FILE *fp;
   const char *word;
   bool fMatch = false;

   if( !( fp = fopen( BAN_LIST, "r" ) ) )
      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 );
               fp = NULL;
               log_string( "Done." );
               return;
            }
            break;

         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 )
      {
         bug( "%s: no match: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   fclose( fp );
   fp = NULL;
   bug( "%s: END wasn't found, but finished anyways.", __FUNCTION__ );
}

/* Saves all bans, for sites, classes and races. Shaddai */
void save_banlist( void )
{
   BAN_DATA *pban;
   FILE *fp;

   if( !first_ban && !first_ban_race && !first_ban_class )
   {
      remove_file( BAN_LIST );
      return;
   }

   if( !( fp = fopen( BAN_LIST, "w" ) ) )
   {
      bug( "%s: Can't open %s", __FUNCTION__, BAN_LIST );
      perror( BAN_LIST );
      return;
   }

   /* Print out all the site bans */
   for( pban = first_ban; pban; pban = pban->next )
   {
      fprintf( fp, "SITE\n" );
      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 );
   fp = NULL;
}

/* The main command for ban, lots of arguments so be carefull what you change here. Shaddai */
CMDF( do_ban )
{
   BAN_DATA *pban;
   char arg1[MIL], arg2[MIL], arg3[MIL], arg4[MIL], *temp;
   int value = 0, btime;

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

   if( !ch->desc )
   {
      bug( "%s: no descriptor", __FUNCTION__ );
      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 != NULL && arg4[0] != '\0' && is_number( arg4 ) )
      btime = atoi( arg4 );
   else
      btime = -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( btime != -1 && ( btime < 1 || btime > 1000 ) )
   {
      send_to_char( "Time value is -1 (forever) or from 1 to 1000.\r\n", ch );
      return;
   }

   /* Need to be carefull with sub-states or everything will get messed up. */
   switch( ch->substate )
   {
      default:
         bug( "%s: illegal substate", __FUNCTION__ );
         return;

      case SUB_RESTRICTED:
         send_to_char( "You can't use this command from within another command.\r\n", 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, (char *)"", (char *)"", 0, 0 );
         return;
   }
   if( arg1 == NULL || arg1[0] == '\0' )
      goto Usage_message;

   /* If no args are sent after the class/site/race, show the current banned items. Shaddai */
   if( !str_cmp( arg1, "site" ) )
   {
      if( arg2 == NULL || arg2[0] == '\0' )
      {
         show_bans( ch, BAN_SITE );
         return;
      }

      /* Are they high enough to ban sites? */
      if( get_trust( ch ) < sysdata.ban_site )
      {
         ch_printf( ch, "You must be %d level to add bans.\r\n", sysdata.ban_site );
         return;
      }
      if( arg3 == NULL || arg3[0] == '\0' )
         goto Usage_message;
      if( !add_ban( ch, arg2, arg3, btime, BAN_SITE ) )
         return;
   }
   else if( !str_cmp( arg1, "race" ) )
   {
      if( arg2 == NULL || arg2[0] == '\0' )
      {
         show_bans( ch, BAN_RACE );
         return;
      }

      /* Are they high enough level to ban races? */
      if( get_trust( ch ) < sysdata.ban_race )
      {
         ch_printf( ch, "You must be %d level to add bans.\r\n", sysdata.ban_race );
         return;
      }
      if( arg3 == NULL || arg3[0] == '\0' )
         goto Usage_message;
      if( !add_ban( ch, arg2, arg3, btime, BAN_RACE ) )
         return;
   }
   else if( !str_cmp( arg1, "class" ) )
   {
      if( arg2 == NULL || arg2[0] == '\0' )
      {
         show_bans( ch, BAN_CLASS );
         return;
      }

      /* Are they high enough to ban classes? */
      if( get_trust( ch ) < sysdata.ban_class )
      {
         ch_printf( ch, "You must be %d level to add bans.\r\n", sysdata.ban_class );
         return;
      }
      if( arg3 == NULL || arg3[0] == '\0' )
         goto Usage_message;
      if( !add_ban( ch, arg2, arg3, btime, BAN_CLASS ) )
         return;
   }
   else if( !str_cmp( arg1, "show" ) )
   {
      /* This will show the note attached to a ban */
      if( arg2 == NULL || arg3 == NULL || arg2[0] == '\0' || arg3[0] == '\0' )
         goto Usage_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?\r\n", ch );
            return;
         }
         if( ( value = atoi( temp ) ) < 1 )
         {
            send_to_char( "You must specify a number greater than 0.\r\n", ch );
            return;
         }
      }
      else if( is_number( arg3 ) )
      {
         if( ( value = atoi( arg3 ) ) < 1 )
         {
            send_to_char( "The number has to be above 0.\r\n", 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 Usage_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.\r\n", ch );
         return;
      }
      ch_printf( ch, "Banned by: %s\r\n", pban->ban_by );
      send_to_char( pban->note, ch );
      return;
   }
   else
      goto Usage_message;
   return;

   /*
    * Catch all Usage message, make sure that return stays above this or you
    * will get the Usage message everytime you issue the command even if it
    * is a valid one.  Shaddai
    */
   Usage_message:
      send_to_char( "Usage: ban site/race/class [<address/race/class> <type> <duration>]\r\n", ch );
      send_to_char( "Usage: ban show site/race/class <address/race/class/#>\r\n", ch );
      send_to_char( "Duration is the length of the ban in days.\r\n", ch );
      send_to_char( "Type can be: newbie, mortal, all, warn or level.\r\n", ch );
      return;
}

/* Allow a already banned site/class or race.  Shaddai */
CMDF( do_allow )
{
   BAN_DATA *pban;
   char arg1[MIL], arg2[MIL], *temp = NULL;
   int value = 0;
   bool fMatch = false;

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

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

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

   set_char_color( AT_IMMORT, ch );

   if( arg1 == NULL || arg1[0] == '\0' || arg2 == NULL || arg2[0] == '\0' )
      goto Usage_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?\r\n", ch );
         return;
      }
      value = atoi( temp );
   }
   else if( is_number( arg2 ) )
   {
      if( ( value = atoi( arg2 ) ) < 1 )
      {
         send_to_char( "The number has to be above 0.\r\n", ch );
         return;
      }
   }

   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\r\n", ch );
            send_to_char( "If you are trying to allow by number use #\r\n", 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 Usage_message;

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

   /* Make sure that return above stays in! */
   Usage_message:
      send_to_char( "Usage: allow site/race/class <address/race/class>\r\n", ch );
      return;
}

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

   /* Don't want mobs or link-deads doing this. */
   if( is_npc( ch ) )
   {
      send_to_char( "Monsters are too dumb to do that!\r\n", ch );
      return;
   }

   if( !ch->desc )
   {
      bug( "%s: no descriptor", __FUNCTION__ );
      return;
   }

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

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

   if( arg2[0] == '#' )
   {
      name = arg2;
      name++;
      if( !is_number( name ) )
         goto Usage_message;
      if( ( count = atoi( name ) ) < 1 )
      {
         send_to_char( "The number has to be above 0.\r\n", ch );
         return;
      }
   }
   else if( is_number( arg2 ) )
   {
      if( ( count = atoi( arg2 ) ) < 1 )
      {
         send_to_char( "The number has to be above 0.\r\n", 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 Usage_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.\r\n", ch );
         }
         else
         {
            pban->warn = false;
            send_to_char( "Warn turned off.\r\n", ch );
         }
      }
      else
      {
         pban->warn = true;
         send_to_char( "Warn turned on.\r\n", ch );
      }
      save_banlist( );
   }
   else
   {
      ch_printf( ch, "%s was not found in the ban list.\r\n", arg2 );
      return;
   }
   return;

   /* The above return has to stay in! */
   Usage_message:
      send_to_char( "Usage: warn site/race/class <address/race/class/#>\r\n", 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 btime, int type )
{
   BAN_DATA *pban, *temp;
   char arg[MSL], *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( "%s: illegal substate", __FUNCTION__ );
         return 0;

      case SUB_RESTRICTED:
         send_to_char( "You can't use this command from within another command.\r\n", ch );
         return 0;

      case SUB_NONE:
      {
         one_argument( arg1, arg );

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

         if( is_number( arg2 ) )
         {
            level = atoi( arg2 );
            if( level < 0 || level > MAX_LEVEL )
            {
               ch_printf( ch, "Level range is from 0 to %d.\r\n", MAX_LEVEL );
               return 0;
            }
         }
         else if( !str_cmp( arg2, "all" ) )
            level = MAX_LEVEL;
         else if( !str_cmp( arg2, "newbie" ) )
            level = 1;
         else if( !str_cmp( arg2, "mortal" ) )
            level = MAX_LEVEL;
         else if( !str_cmp( arg2, "warn" ) )
            level = BAN_WARN;
         else
         {
            bug( "%s: Bad string [%s] for flag.", __FUNCTION__, arg2 );
            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]->name, arg ) )
                        break;
                  value = i;
               }
               if( value < 0 || value >= MAX_CLASS )
               {
                  send_to_char( "Unknown class.\r\n", 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.\r\n", ch );
                        return 0;
                     }
                     else
                     {
                        temp->level = level;
                        if( temp->level == BAN_WARN )
                           temp->warn = true;
                        temp->ban_time = STRALLOC( distime( current_time ) );
                        if( btime > 0 )
                        {
                           temp->duration = btime;
                           temp->unban_date = ( current_time + ( 86400 * btime ) );
                        }
                        else
                        {
                           temp->duration = -1;
                           temp->unban_date = -1;
                        }
                        STRSET( temp->ban_by, ch->name );
                        send_to_char( "Updated entry.\r\n", ch );
                        return 1;
                     }
                  }
               }
               CREATE( pban, BAN_DATA, 1 );
               pban->name = STRALLOC( class_table[value]->name );
               pban->flag = value;
               pban->level = level;
               pban->ban_by = STRALLOC( 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]->name, arg ) )
                        break;
                  value = i;
               }
               if( value < 0 || value >= MAX_RACE )
               {
                  send_to_char( "Unknown race.\r\n", 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.\r\n", ch );
                        return 0;
                     }
                     else
                     {
                        temp->level = level;
                        if( temp->level == BAN_WARN )
                           temp->warn = true;
                        temp->ban_time = STRALLOC( distime( current_time ) );
                        if( btime > 0 )
                        {
                           temp->duration = btime;
                           temp->unban_date = ( current_time + ( 86400 * btime ) );
                        }
                        else
                        {
                           temp->duration = -1;
                           temp->unban_date = -1;
                        }
                        STRSET( temp->ban_by, ch->name );
                        send_to_char( "Updated entry.\r\n", ch );
                        return 1;
                     }
                  }
               }
               CREATE( pban, BAN_DATA, 1 );
               pban->name = STRALLOC( race_table[value]->name );
               pban->flag = value;
               pban->level = level;
               pban->ban_by = STRALLOC( 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;
               size_t x;

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

               if( !name ) /* Double check to make sure name isnt null */
               {
                  /* Free this stuff if its there */
                  if( user_name )
                  {
                     STRFREE( temp_host );
                     STRFREE( temp_user );
                  }
                  send_to_char( "Name was null.\r\n", ch );
                  return 0;
               }

               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 ) )
                     {
                        /* Free this stuff if its there */
                        if( user_name )
                        {
                           STRFREE( temp_host );
                           STRFREE( temp_user );
                        }
                        send_to_char( "That entry already exists.\r\n", ch );
                        return 0;
                     }
                     else
                     {
                        temp->suffix = suffix;
                        temp->prefix = prefix;
                        if( temp->level == BAN_WARN )
                           temp->warn = true;
                        temp->level = level;
                        temp->ban_time = STRALLOC( distime( current_time ) );
                        if( btime > 0 )
                        {
                           temp->duration = btime;
                           temp->unban_date = ( current_time + ( 86400 * btime ) );
                        }
                        else
                        {
                           temp->duration = -1;
                           temp->unban_date = -1;
                        }
                        if( user_name )
                        {
                           STRFREE( temp_host );
                           STRFREE( temp_user );
                        }
                        STRSET( temp->ban_by, ch->name );
                        send_to_char( "Updated entry.\r\n", ch );
                        return 1;
                     }
                  }
               }
               CREATE( pban, BAN_DATA, 1 );
               pban->ban_by = STRALLOC( ch->name );
               pban->suffix = suffix;
               pban->prefix = prefix;
               pban->name = STRALLOC( name );
               pban->level = level;
               LINK( pban, first_ban, last_ban, next, prev );
               if( user_name )
               {
                  STRFREE( temp_host );
                  STRFREE( temp_user );
               }
               break;
            }
            default:
               bug( "%s: Bad type %d.", __FUNCTION__, type );
               return 0;
         }
         pban->ban_time = STRALLOC( distime( current_time ) );
         if( btime > 0 )
         {
            pban->duration = btime;
            pban->unban_date = ( current_time + ( 86400 * btime ) );
         }
         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;
         start_editing( ch, pban->note );
         return 1;
      }
      case SUB_BAN_DESC:
         pban = ( BAN_DATA * ) ch->dest_buf;
         if( !pban )
         {
            bug( "%s: sub_ban_desc: NULL ch->dest_buf", __FUNCTION__ );
            ch->substate = SUB_NONE;
            return 0;
         }
         STRFREE( pban->note );
         pban->note = copy_buffer( ch );
         stop_editing( ch );
         ch->substate = ch->tempnum;
         save_banlist( );
         if( pban->duration > 0 )
            ch_printf( ch, "%s banned for %d days.\r\n", pban->name, pban->duration );
         else
            ch_printf( ch, "%s banned forever.\r\n", pban->name );
         return 1;
   }
}

/* 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[MSL];
   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 != MAX_LEVEL )
         continue;
      if( pban->prefix && pban->suffix && strstr( new_host, pban->name ) )
      {
         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;
}

bool check_expire( BAN_DATA *pban )
{
   if( pban->unban_date < 0 )
      return false;
   if( pban->unban_date <= current_time )
   {
      log_printf_plus( LOG_WARN, sysdata.perm_log, "%s ban has expired.", pban->name );
      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( "%s: Unknown Ban Type %d.", __FUNCTION__, 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 );
}

void free_bans( void )
{
   BAN_DATA *ban, *ban_next;

   for( ban = first_ban; ban; ban = ban_next )
   {
      ban_next = ban->next;
      dispose_ban( ban, BAN_SITE );
   }
   for( ban = first_ban_race; ban; ban = ban_next )
   {
      ban_next = ban->next;
      dispose_ban( ban, BAN_RACE );
   }
   for( ban = first_ban_class; ban; ban = ban_next )
   {
      ban_next = ban->next;
      dispose_ban( ban, BAN_CLASS );
   }
}

bool check_race_bans( int iRace )
{
   BAN_DATA *pban;

   for( pban = first_ban_race; pban; pban = pban->next )
   {
      if( pban->flag == iRace )
      {
         if( check_expire( pban ) )
         {
            dispose_ban( pban, BAN_RACE );
            save_banlist( );
            return false;
         }
         return true;
      }
   }
   return false;
}

bool check_class_bans( int iClass )
{
   BAN_DATA *pban;

   for( pban = first_ban_class; pban; pban = pban->next )
   {
      if( pban->flag == iClass )
      {
         if( check_expire( pban ) )
         {
            dispose_ban( pban, BAN_CLASS );
            save_banlist( );
            return false;
         }
         return true;
      }
   }
   return false;
}