/
swrfuss/
swrfuss/boards/
swrfuss/color/
swrfuss/doc/mudprogs/
swrfuss/email/
swrfuss/planets/
swrfuss/space/
/***************************************************************************
*                           STAR WARS REALITY 1.0                          *
*--------------------------------------------------------------------------*
* Star Wars Reality Code Additions and changes from the Smaug Code         *
* copyright (c) 1997 by Sean Cooper                                        *
* -------------------------------------------------------------------------*
* Starwars and Starwars Names copyright(c) Lucas Film Ltd.                 *
*--------------------------------------------------------------------------*
* SMAUG 1.0 (C) 1994, 1995, 1996 by Derek Snider                           *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,                    *
* Scryn, Rennard, Swordbearer, Gorog, Grishnakh and Tricops                *
* ------------------------------------------------------------------------ *
* 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.     *
* ------------------------------------------------------------------------ *
*                            Account Management                            *
****************************************************************************/

#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/wait.h>
#include <zlib.h>
#include "mud.h"
#include "sha256.h"

/* TCP Defines */
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netdb.h>

const char account_echo_off_str[] = { ( char ) IAC, ( char ) WILL, TELOPT_ECHO, '\0' };
const char account_echo_on_str[] = { ( char ) IAC, ( char ) WONT, TELOPT_ECHO, '\0' };

/* Local Defines */
bool check_parse_name args( ( char *name ) );
bool check_playing args( ( DESCRIPTOR_DATA * d, char *name, bool kick ) );
bool check_reconnect args( ( DESCRIPTOR_DATA * d, char *name, bool fConn ) );
void show_title( DESCRIPTOR_DATA * d );


/*******************************************************************************
***************************** Dependant Functions ******************************
*******************************************************************************/

char *smash_color( char *str )
{
   static char ret[MAX_STRING_LENGTH];
   char *retptr;
   retptr = ret;

   for( ; *str != '\0'; str++ )
   {
      if( *str == '&' )
         str++;
      else
      {
         *retptr = *str;
         retptr++;
      }
   }

   *retptr = '\0';
   return ret;
}


/*******************************************************************************
******************************* Account Function *******************************
*******************************************************************************/

bool has_account( CHAR_DATA * ch )
{
   if( IS_NPC( ch ) )
      return FALSE;
   if( ch->desc && ch->desc->account )
      return TRUE;

   return FALSE;
}


bool account_exist( char *name )
{
   char strsave[MAX_INPUT_LENGTH];
   FILE *fp;

   sprintf( strsave, "%s%c/%s", ACCOUNT_DIR, tolower( name[0] ), capitalize( name ) );

   if( ( fp = fopen( strsave, "r" ) ) != NULL )
   {
      fclose( fp );
      return TRUE;
   }
   else
      return FALSE;
}


bool acc_char_playing( char *name )
{
   CHAR_DATA *ch;

   if( !name )
      return FALSE;

   for( ch = first_char; ch; ch = ch->next )
      if( !str_cmp( ch->name, name ) && !IS_NPC( ch ) )
         return TRUE;

   return FALSE;
}


ACC_CHAR_DATA *get_char_account( ACCOUNT_DATA * account, char *name )
{
   ACC_CHAR_DATA *ch;

   for( ch = account->first_acc_char; ch; ch = ch->next )
      if( !str_cmp( ch->name, name ) )
         return ch;

   return NULL;
}


void dispose_acc_char( ACCOUNT_DATA * account, ACC_CHAR_DATA * ch )
{
   if( !ch )
      return;

   if( ch->name )
      STRFREE( ch->name );
   if( ch->clan )
      STRFREE( ch->clan );
   if( ch->password )
      STRFREE( ch->password );
   if( ch->quit_location )
      STRFREE( ch->quit_location );

   UNLINK( ch, account->first_acc_char, account->last_acc_char, next, prev );
   DISPOSE( ch );
}


void dispose_account( ACCOUNT_DATA * account )
{
   ACC_CHAR_DATA *ch, *next_ch;

   if( !account )
      return;

   if( account->name )
      STRFREE( account->name );

   for( ch = account->first_acc_char; ch; ch = next_ch )
   {
      next_ch = ch->next;
      dispose_acc_char( account, ch );
   }

   UNLINK( account, first_account, last_account, next, prev );
   DISPOSE( account );
}


bool check_multi_account( DESCRIPTOR_DATA * d )
{
   return FALSE;
}


/*******************************************************************************
******************************* Account Creation *******************************
*******************************************************************************/

ACCOUNT_DATA *create_account( char *name )
{
   ACCOUNT_DATA *account;

   if( !name )
      return NULL;

   CREATE( account, ACCOUNT_DATA, 1 );
   LINK( account, first_account, last_account, next, prev );

   account->name = STRALLOC( name );
   account->maxalts = 1;

   return account;
}


void save_account( ACCOUNT_DATA * account )
{
   FILE *fp;
   ACC_CHAR_DATA *ch;
   char filename[256];

   if( !account )
      return;

   sprintf( filename, "%s%c/%s", ACCOUNT_DIR, tolower( account->name[0] ), capitalize( account->name ) );
   if( ( fp = fopen( filename, "w" ) ) != NULL )
   {
      fprintf( fp, "Name           %s~\n\r", account->name );
      fprintf( fp, "Email          %s~\n\r", account->email );
      fprintf( fp, "Password       %s~\n\r", account->password );
      for( ch = account->first_acc_char; ch; ch = ch->next )
         fprintf( fp, "Char           %s~\n\r", ch->name );
      fprintf( fp, "Host           %s~\n\r", account->host );
      fprintf( fp, "Last_played    %s~\n\r", account->last_played );
      fprintf( fp, "Timer          %d\n\r", ( int ) account->timer );
      fprintf( fp, "Multiplay      %d\n\r", account->multiplay );
      fprintf( fp, "Banned         %d\n\r", account->banned );
      fprintf( fp, "Points         %d\n\r", account->points );
      fprintf( fp, "Maxalts        %d\n\r", account->maxalts );
      fprintf( fp, "Verify         %d\n\r", account->verify );
      fprintf( fp, "Verified       %d\n\r", account->verified );
      fprintf( fp, "Attempts       %d\n\r", account->attempts );
      fprintf( fp, "PasswordFail   %d\n\r", account->passwordfail );
      fprintf( fp, "End\n\r" );
      fclose( fp );
   }
}


ACCOUNT_DATA *fread_account( char *name )
{
   FILE *fp;
   ACCOUNT_DATA *account;
   char filename[256], *word;
   bool fMatch = FALSE;

   if( !name )
      return NULL;

   sprintf( filename, "%s%c/%s", ACCOUNT_DIR, tolower( name[0] ), capitalize( name ) );
   if( ( fp = fopen( filename, "r" ) ) != NULL )
   {
      CREATE( account, ACCOUNT_DATA, 1 );
      LINK( account, first_account, last_account, next, prev );

      account->alts = 0;
      account->immortal = FALSE;

      for( ;; )
      {
         word = feof( fp ) ? ( char * ) "End" : fread_word( fp );
         fMatch = FALSE;

         switch ( UPPER( word[0] ) )
         {
            case 'A':
               KEY( "Attempts", account->attempts, fread_number( fp ) );
               break;

            case 'B':
               KEY( "Banned", account->banned, fread_number( fp ) );
               break;

            case 'C':
               if( !str_cmp( word, "Char" ) )
               {
                  char *string = fread_string( fp );

                  add_acc_char( account, string, FALSE, FALSE );
                  ++account->alts;

                  if( string )
                     STRFREE( string );
                  fMatch = TRUE;
                  break;
               }
               break;

            case 'E':
               KEY( "Email", account->email, fread_string_nohash( fp ) );
               if( !str_cmp( word, "End" ) )
                  return account;
               break;

            case 'H':
               KEY( "Host", account->host, fread_string( fp ) );
               break;

            case 'L':
               KEY( "Last_played", account->last_played, fread_string( fp ) );
               break;

            case 'M':
               KEY( "Maxalts", account->maxalts, fread_number( fp ) );
               KEY( "Multiplay", account->multiplay, fread_number( fp ) );
               break;

            case 'N':
               KEY( "Name", account->name, fread_string( fp ) );
               break;

            case 'P':
               KEY( "Password", account->password, fread_string_nohash( fp ) );
               KEY( "PasswordFail", account->passwordfail, fread_number( fp ) );
               KEY( "Points", account->points, fread_number( fp ) );
               break;

            case 'T':
               KEY( "Timer", account->timer, fread_number( fp ) );
               break;

            case 'V':
               KEY( "Verified", account->verified, fread_number( fp ) );
               KEY( "Verify", account->verify, fread_number( fp ) );
               break;
         }

         if( !fMatch )
            bug( "Fread_account: No match '%s'", 0, word );
      }

      fclose( fp );
   }
   else
      return NULL;

   return account;
}

void fread_acc_char( ACC_CHAR_DATA * ch, FILE * fp )
{
   ROOM_INDEX_DATA *room;
   char *word;
   bool fMatch = FALSE;
   int room_num = 0;

   for( ;; )
   {
      word = feof( fp ) ? ( char * ) "End" : fread_word( fp );
      fMatch = FALSE;

      switch( UPPER( word[0] ) )
      {
         case '*':
            fMatch = TRUE;
            fread_to_eol( fp );
            break;

         case 'C':
            KEY( "Clan", ch->clan, fread_string( fp ) );
            break;

         case 'E':
            if( !str_cmp( word, "End" ) )
            {
               if( ( room = get_room_index( room_num ) ) != NULL )
                  ch->quit_location = STRALLOC( room->name );
               return;
            }
            break;

         case 'P':
            KEY( "Password", ch->password, fread_string( fp ) );
            break;

         case 'R':
            KEY( "Race", ch->race, fread_number( fp ) );
            KEY( "Room", room_num, fread_number( fp ) );
            break;

         case 'T':
            KEY( "Toplevel", ch->level, fread_number( fp ) );
            break;
      }

      if( !fMatch )
         fread_to_eol( fp );
   }
}


/*******************************************************************************
******************************* Account Handlers *******************************
*******************************************************************************/

bool add_acc_char( ACCOUNT_DATA * account, char *name, bool pending, bool newbie )
{
   ACC_CHAR_DATA *ch;
   FILE *fp;
   char filename[256];

   if( !account || !name )
      return FALSE;

   if( newbie )
   {
      CREATE( ch, ACC_CHAR_DATA, 1 );
      LINK( ch, account->first_acc_char, account->last_acc_char, next, prev );

      ch->name = STRALLOC( name );
      ch->race = 0;
      ch->password = STRALLOC( "" );
      ch->quit_location = STRALLOC( "Unknown" );
      ch->level = 1;

      return TRUE;
   }

   sprintf( filename, "%s%c/%s", PLAYER_DIR, tolower( name[0] ), name );
   if( ( fp = fopen( filename, "r" ) ) != NULL )
   {
      CREATE( ch, ACC_CHAR_DATA, 1 );
      LINK( ch, account->first_acc_char, account->last_acc_char, next, prev );

      ch->name = STRALLOC( name );
      fread_acc_char( ch, fp );

      if( pending )
         account->pending = ch;
      else
      {
         if( ch->level >= LEVEL_IMMORTAL )
         {
            account->immortal = TRUE;
            account->maxalts = 5;
            account->points = 1000;
         }
      }
      fclose( fp );
      return TRUE;
   }
   else
      return FALSE;
}


/*******************************************************************************
******************************* Account Colorize *******************************
*******************************************************************************/

void acolorize( char *in, char *out )
{
   char mesg[MAX_STRING_LENGTH];
   char buf [MAX_STRING_LENGTH];
   char *start;

   start = &in[0];
   sprintf( mesg, "&W" );

   while( *start)
   {
      if( ispunct( ( int ) * start ) && *start != '&' )
      {
         sprintf( buf, "&B%c&W", *start );
         strcat( mesg, buf );
      }
      else
      {
         sprintf( buf, "%c", *start );
         strcat( mesg, buf );
      }

      ++start;
   }

   strcpy( out, mesg );
   return;
}


void ad_printf( DESCRIPTOR_DATA * d, char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2];
   char buf2[MAX_STRING_LENGTH * 2];

   va_list args;

   va_start( args, fmt );
   vsprintf( buf, fmt, args );
   va_end( args );

   acolorize( buf, buf2 );

   send_to_desc_color( buf2, d );
   return;
}


/*******************************************************************************
******************************** Account Display *******************************
*******************************************************************************/

void display_who( DESCRIPTOR_DATA * d )
{
   char buf[MAX_STRING_LENGTH];
   int count = 0;
   DESCRIPTOR_DATA *d2;

   for( d2 = last_descriptor; d2; d2 = d2->prev )
   {
      if( d2->connected != CON_PLAYING && d2->connected != CON_EDITING )
         continue;
      if( IS_IMMORTAL( d2->character ) && IS_SET( d2->character->act, PLR_WIZINVIS ) && !d->account->immortal )
         continue;

      sprintf( buf, "%s - %s\n\r", IS_IMMORTAL( d2->character ) ? "Immortal" : "Player", d2->character->pcdata->title );
      ad_printf( d, buf );
      count++;
   }

   if( !count )
      ad_printf( d, "\n\rNobody playing.\n\r" );
}


void display_account_menu( DESCRIPTOR_DATA * d )
{
   ad_printf( d, "\n\rEmail Address:  &W" );
   ad_printf( d, d->account->email );
   ad_printf( d, "\n\r" );
   ad_printf( d, "Account Name:   %s\n\r", d->account->name );
   ad_printf( d, "Account Status: %s", d->account->verified ? "Verified" : "Not Verified" );
   ad_printf( d, "\n\r\n\r" );
   ad_printf( d, "Play     - plays designated character\n\r" );
   ad_printf( d, "Create   - creates a character to add to account\n\r" );
   ad_printf( d, "Add      - adds an existing character to account\n\r" );
   ad_printf( d, "Remove   - removes and deletes chracter from account\n\r" );
   ad_printf( d, "Delete   - this will delete your account and chracters\n\r" );
   ad_printf( d, "Password - change your account password\n\r" );
   ad_printf( d, "List     - lists the characters currently in your account\n\r" );
   ad_printf( d, "Menu     - displays this menu\n\r" );
   if( d->account->verified )
      ad_printf( d, "Points   - displays how many account points you currently have\n\r" );
   if( d->account->verified )
      ad_printf( d, "Who      - see who is on the game\n\r" );
   ad_printf( d, "Quit     - terminates connection to your MUD\n\r" );
   return;
}


void display_account_chars( DESCRIPTOR_DATA * d )
{
   ACC_CHAR_DATA *ch;
   char buf[MAX_STRING_LENGTH];

   if( !d || !d->account )
      return;

   ad_printf( d, "\n\rYou have %d characters on your account. Maximum limit is %d.\n\r", d->account->alts, d->account->maxalts );
   ad_printf( d, "\n\rCharacter       Race            Clan                 Level   Quit Room\n\r" );

   for( ch = d->account->first_acc_char; ch; ch = ch->next )
   {
      sprintf( buf, "%-15s %-15s %-20s %-5d   %-20s\n\r", ch->name, npc_race[ch->race], ch->clan ? ch->clan : "None", ch->level, ch->quit_location ? smash_color( ch->quit_location ) : "None" );
      ad_printf( d, buf );
   }

   ad_printf( d, "\n\r" );

   if( d->account->multiplay )
      ad_printf( d, "This account may multiplay.\n\r" );
   if( d->account->immortal )
      ad_printf( d, "This account has immortal status.\n\r" );

   return;
}


/*******************************************************************************
******************************** Account Interp ********************************
*******************************************************************************/

void account_interp( DESCRIPTOR_DATA * d, char *cmdline )
{
   ACC_CHAR_DATA *ch, *acc_char, *check;
   DESCRIPTOR_DATA *desc;
   char *arg;
   bool chk = FALSE;

   switch( d->connected )
   {
      case CON_GET_ACCOUNT_NAME:
         if( cmdline[0] == '\0' )
         {
            close_socket( d, FALSE );
            return;
         }

         cmdline[0] = UPPER( cmdline[0] );

         if( !check_parse_name( cmdline ) )
         {
            ad_printf( d, "\n\rIllegal name, try again.\nEnter account name: &W" );
            return;
         }

         if( strlen( cmdline ) < 4 )
         {
            ad_printf( d, "\n\r\n\rAccount name is too short.\nEnter account name: &W\n\r" );
            return;
         }

         if( strlen( cmdline ) > 15 )
         {
            ad_printf( d, "\n\rAccount name cannot be longer than 15 characters.\nEnter account name: &W\n\r" );
            return;
         }

         if( ( d->account = fread_account( cmdline ) ) != NULL )
         {
            if( d->account->attempts >= 5 )
            {
               sprintf( log_buf, "Locked account has been blocked: %s\n\r", d->account->name );
               to_channel( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
               ad_printf( d, "\n\rThis account has been locked for security purposes.\n\r" );
               close_socket( d, FALSE );
            }

            if( d->account->banned )
            {
               sprintf( log_buf, "Banned account has been blocked: %s\n\r", d->account->name );
               to_channel( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
               ad_printf( d, "\n\rThis account has been banned.\n\r" );
               close_socket( d, FALSE );
            }

            if( !d->account->multiplay )
            {
               for( desc = first_descriptor; desc; desc = desc->next )
               {
                  if( d != desc && desc->account && !str_cmp( d->host, desc->host ) && str_cmp( cmdline, desc->account->name ) && !str_cmp( desc->host, d->host ) )
                  {
                     ad_printf( d, "\n\rSorry, multiplaying is not allowed... Have your other character quit first!\n\r" );
                     close_socket( d, FALSE );
                  }
               }
            }

            ad_printf( d, "\n\rEnter account password: " );
            write_to_buffer( d, account_echo_off_str, 0 );
            d->connected = CON_GET_ACCOUNT_OLD_PASS;
         }
         else
         {
            for( desc = first_descriptor; desc; desc = desc->next )
            {
               if( d != desc && desc->account && !str_cmp( d->host, desc->host ) && str_cmp( cmdline, desc->account->name ) && !str_cmp( desc->host, d->host ) )
               {
                  ad_printf( d, "\n\rSorry, multiplaying is not allowed... Have your other character quit first!\n\r" );
                  close_socket( d, FALSE );
               }
            }

            ad_printf( d, "\n\rAccount not found, we will create an account for the name provided.\nDo you accept this name? [Y/N]: " );
            d->account = create_account( cmdline );
            d->connected = CON_GET_ACCOUNT_NAME_CONFIRM;
         }
         break;

      case CON_GET_ACCOUNT_OLD_PASS:
         if( !strcmp( sha256_crypt( cmdline ), d->account->password ) )
         {
            if( d->account->name )
               ad_printf( d, "\n\r\n\rWelcome back %s!", d->account->name );

            if( d->account->host )
            {
               ad_printf( d, "\n\r\n\rLast logged in from: " );
               ad_printf( d, d->account->host );
            }

            if( d->account->last_played )
            {
               ad_printf( d, "\n\rLast character played: " );
               ad_printf( d, d->account->last_played );
               ad_printf( d, "\n\r" );
            }

            sprintf( log_buf, "Loading account: %s", d->account->name );
            log_string( log_buf );

            display_account_menu( d );
            d->account->attempts = 0;
            ad_printf( d, "\n\rAccount> &W" );
            if( d->account->host )
               STRFREE( d->account->host );
            d->account->host = STRALLOC( d->host );
            write_to_buffer( d, account_echo_on_str, 0 );
            d->connected = CON_ACCOUNT_PENDING;
         }
         else
         {
            ad_printf( d, "\n\rInvalid password, access denied.\n\r" );
            d->account->attempts++;
            save_account( d->account );
            close_socket( d, FALSE );
            return;
         }
         break;

      case CON_GET_ACCOUNT_NAME_CONFIRM:
         switch( cmdline[0] )
         {
            case 'y':
            case 'Y':
               ad_printf( d, "\n\rIs this your only account? &W" );
               d->connected = CON_GET_ACCOUNT_FIRST;
               break;

            case 'n':
            case 'N':
               ad_printf( d, "\n\rEnter your desired account name: &W" );
               dispose_account( d->account );
               d->account = NULL;
               d->connected = CON_GET_ACCOUNT_NAME;
               break;

            default:
               ad_printf( d, "\n\rDo you accept this account name? [Y/N]: &W" );
               break;
         }
         break;

      case CON_GET_ACCOUNT_FIRST:
         switch( cmdline[0] )
         {
            case 'y':
            case 'Y':
               if( check_multi_account( d ) == TRUE )
               {
                  ad_printf( d, "\n\rAlternate account detected. You may only have one account. &W\n\r" );
                  close_socket( d, FALSE );
                  break;
               }
               sprintf( log_buf, "Creating new account: %s", d->account->name );
               log_string( log_buf );
               ad_printf( d, "\n\rEnter an email address: &W" );
               write_to_buffer( d, account_echo_off_str, 0 );
               d->connected = CON_ACCOUNT_GET_EMAIL;
               break;

            case 'n':
            case 'N':
               ad_printf( d, "\n\rSorry, you may only have one account. &W\n\r" );
               close_socket( d, FALSE );
               break;

            default:
               ad_printf( d, "\n\rIs this your only account, yes or no? [Y/N]: &W" );
               break;
         }
         break;

      case CON_ACCOUNT_GET_EMAIL:
         if( strlen( cmdline ) < 8 )
         {
            ad_printf( d, "\n\rYour email address is too short to be a valid email.\n\r\n\rEnter an email address: &W" );
            return;
         }

         if( !strchr( cmdline, '@' ) )
         {
            ad_printf( d, "\n\rEmail address does not contain @ symbol. Not valid.\n\r\n\rEnter an email address: &W" );
            return;
         }

         if( !strchr( cmdline, '.' ) )
         {
            ad_printf( d, "\n\rEmail address is not of proper format. Ex. example@email.com\n\r\n\rEnter an email address: &W" );
            return;
         }

         if( strchr( cmdline, '~' ) )
         {
            ad_printf( d, "\n\rEmail address cannot contain tilde symbol.\n\r\n\rEnter an email address: &W" );
            return;
         }

         d->account->email = STRALLOC( cmdline );
         d->account->last_played = STRALLOC( "" );
         ad_printf( d, "\n\rEnter email address again: &W" );
         write_to_buffer( d, account_echo_off_str, 0 );
         d->connected = CON_ACCOUNT_CONFIRM_EMAIL;
         break;

      case CON_ACCOUNT_CONFIRM_EMAIL:
         if( strcmp( ( cmdline ), d->account->email ) )
         {
            ad_printf( d, "\n\r\n\rEmail addresses do not match.\n\r\n\rEnter an email address: &W" );
            d->connected = CON_ACCOUNT_GET_EMAIL;
            STRFREE( d->account->email );
            return;
         }
         ad_printf( d, "\n\r\n\rEnter a password: " );
         write_to_buffer( d, account_echo_off_str, 0 );
         d->connected = CON_GET_ACCOUNT_NEW_PASS;
         break;

      case CON_GET_ACCOUNT_NEW_PASS:
         if( strlen( cmdline ) < 5 )
         {
            ad_printf( d, "\n\rThe password must be at least 5 characters long.\n\r\n\rEnter a password: &W" );
            return;
         }

         if( strchr( cmdline, '~' ) )
         {
            ad_printf( d, "\n\rPassword may not contain tilde symbol.\n\r\n\rEnter a password: &W" );
            return;
         }

         d->account->password = STRALLOC( sha256_crypt( cmdline ) );
         ad_printf( d, "\n\rEnter password again: &W" );
         d->connected = CON_ACCOUNT_CONFIRM_PASS;
         break;

      case CON_ACCOUNT_CONFIRM_PASS:
         if( strcmp( sha256_crypt( cmdline ), d->account->password ) )
         {
            ad_printf( d, "\n\r\n\rPasswords do not match.\n\r\n\rEnter a password: &W" );
            d->connected = CON_GET_ACCOUNT_NEW_PASS;
            STRFREE( d->account->password );
            return;
         }

         write_to_buffer( d, account_echo_on_str, 0 );
         sprintf( log_buf, "Loading account: %s", d->account->name );
         log_string( log_buf );
         ad_printf( d, "\n\r" );
         save_account( d->account );
         display_account_menu( d );
         ad_printf( d, "\n\rAccount>&W " );
         d->connected = CON_ACCOUNT_PENDING;
         return;

      case CON_ACCOUNT_ADD_CHAR_PASS:
         if( strcmp( sha256_crypt( cmdline ), d->account->pending->password ) )
         {
            ad_printf( d, "\n\rInvalid password. Character not added.\n\r\n\rAccount> &W" );
            sprintf( log_buf, "%s attempted to add %s to account. Wrong password.", d->account->name, d->account->pending->name );
            log_string( log_buf );
            dispose_acc_char( d->account, d->account->pending );
         }
         else
         {
            add_acc_char( d->account, d->account->pending->name, FALSE, FALSE );
            sprintf( log_buf, "%s has added '%s' to their account.", d->account->name, d->account->pending->name );
            log_string( log_buf );
            dispose_acc_char( d->account, d->account->pending );
            ad_printf( d, "\n\rCharacter added.\n\r\n\rAccount> &W" );
            d->account->alts++;
            save_account( d->account );
         }

         d->account->pending = NULL;
         d->connected = CON_ACCOUNT_PENDING;
         write_to_buffer( d, account_echo_on_str, 0 );         
         break;

      case CON_ACCOUNT_PENDING:
         if( ( arg = strchr( cmdline, ' ' ) ) != NULL )
         {
            *arg = '\0';
            ++arg;
         }

         if( nifty_is_name_prefix( cmdline, "play" ) )
         {
            if( !arg )
            {
               ad_printf( d, "\n\rUsage: play <character name>\n\rAccount> &W" );
               return;
            }

            if( ( ch = get_char_account( d->account, arg ) ) == NULL )
            {
               ad_printf( d, "\n\rNo such player. See 'list' for details.\n\rAccount>&W " );
               return;
            }

            if( !d->account->multiplay )
            {
               for( acc_char = d->account->first_acc_char; acc_char; acc_char = acc_char->next )
               {
                  if( !str_cmp( acc_char->name, ch->name ) )
                     continue;

                  if( acc_char_playing( acc_char->name ) )
                  {
                     ad_printf( d, acc_char->name );
                     ad_printf( d, " is already playing. No multiplaying allowed on this account.\n\rAccount> &W" );
                     return;
                  }
               }
            }

            if( check_playing( d, ch->name, FALSE ) == BERR )
            {
               ad_printf( d, "\n\rName: " );
               return;
            }

            if( d->account->timer > current_time && d->account->last_played && !d->account->multiplay && str_cmp( ch->name, d->account->last_played ) )
            {
               char buf[MAX_STRING_LENGTH];
               int ltime = ( int ) difftime( d->account->timer, current_time );

               int minute = ltime / 60;
               int second = ltime % 60;

               sprintf( buf, "You must wait %d minute%s and %d second%s before playing that character.\n\r", minute, minute != 1 ? "s": "", second, second != 1 ? "s" : "" );
               ad_printf( d, buf );
               return;
            }

            if( load_char_obj( d, ch->name, TRUE, TRUE ) )
            {
               char buf[MAX_STRING_LENGTH];

               chk = check_reconnect( d, ch->name, FALSE );

               if( chk == BERR )
                  return;

               if( check_playing( d, ch->name, TRUE ) )
                  return;

               chk = check_reconnect( d, ch->name, TRUE );

               if( chk == BERR )
               {
                  close_socket( d, FALSE );
                  return;
               }

               if( chk == TRUE )
                  return;

               sprintf( buf, "%s", d->character->name );
               d->character->desc = NULL;
               free_char( d->character );
               load_char_obj( d, buf, FALSE, FALSE );

               if( d->account->last_played )
                  STRFREE( d->account->last_played );
               if( d->account->passwordfail > 1 )
                  d->account->passwordfail = 0;
               d->account->last_played = STRALLOC( ch->name );
               save_account( d->account );
               show_title( d );
            }
            else
            {
               ad_printf( d, "\n\rCharacter not found.\n\rAccount> &W" );
            }

            return;
         }

         else if( nifty_is_name_prefix( cmdline, "create" ) )
         {
            if( d->account->alts >= d->account->maxalts )
            {
               ad_printf( d, "\n\rYou may only have '%d' characters.\n\rAccount> &W", d->account->maxalts );
               return;
            }

            sprintf( log_buf, "%s is creating a new character.", d->account->name );
            log_string( log_buf );
            ad_printf( d, "\n\rEnter name for character: &W" );
            d->newstate = 1;
            d->connected = CON_GET_NAME;
            return;
         }

         else if( nifty_is_name_prefix( cmdline, "add" ) )
         {
            if( d->account->alts >= d->account->maxalts )
            {
               ad_printf( d, "\n\rYou have reached your limit on characters.\n\rAccount> &W" );
               return;
            }

            if( !arg || arg[0] == '\0' )
            {
               ad_printf( d, "\n\rAdd which character?\n\rAccount> &W" );
               return;
            }

            arg[0] = toupper( arg[0] );

            for( check = d->account->first_acc_char; check; check = check->next )
            {
               if( !str_cmp( check->name, arg ) )
               {
                  ad_printf( d, "\n\rThat character has already been added to your account.\n\rAccount> &W" );
                  return;
               }
            }

            if( add_acc_char( d->account, arg, TRUE, FALSE ) )
            {
               write_to_buffer( d, account_echo_off_str, 0 );
               ad_printf( d, "\n\rEnter password for character: " );
               d->connected = CON_ACCOUNT_ADD_CHAR_PASS;
            }
            else
               ad_printf( d, "\n\rCharacter not found.\n\rAccount> &W" );

            return;
         }

         else if( nifty_is_name_prefix( cmdline, "remove" ) )
         {
            ACC_CHAR_DATA *rch;
            char buf[MAX_STRING_LENGTH];

            if( !d->account->verified )
            {
               ad_printf( d, "\n\rAccount must be verified before you remove existing characters from it.\n\rAccount> &W" );
               return;
            }

            if( !arg || arg[0] == '\0' )
            {
               ad_printf( d, "\n\rRemove which character?\n\rAccount> &W" );
               return;
            }

            arg[0] = toupper( arg[0] );

            if( ( rch = get_char_account( d->account, arg ) ) == NULL )
            {
               ad_printf( d, "\n\rThat character is not on your account.\n\rAccount> &W" );
               return;
            }

            ad_printf( d, "\n\rPlayer removed from account and deleted.\n\rAccount> &W" );

            sprintf( log_buf, "Deleting character: %s from %s's account.", rch->name, d->account->name );
            log_string( log_buf );

            sprintf( buf, "%s%c/%s", PLAYER_DIR, tolower( rch->name[0] ), rch->name );
            remove( buf );

            d->account->alts--;
            dispose_acc_char( d->account, rch );
            save_account( d->account );

            return;
         }

         else if( nifty_is_name_prefix( cmdline, "delete" ) )
         {
            char account_buf[MAX_STRING_LENGTH];
            char character_buf[MAX_STRING_LENGTH];
            ACC_CHAR_DATA *player;

            if( d->account->passwordfail >= 3 )
            {
               ad_printf( d, "\n\rAccount deletion has been locked due to too many failures.\n\rAccount> &W" );
               return;
            }

            if( !arg )
            {
               ad_printf( d, "\n\rYou must provide your password to delete your account!\n\rAccount> &W" );
               return;
            }

            if( strcmp( sha256_crypt( arg ), d->account->password ) )
            {
               ad_printf( d, "\n\rWrong password. Cannot delete account.\n\rAccount> &W" );
               d->account->passwordfail++;
               save_account( d->account );
               return;
            }
            else
            {
               sprintf( log_buf, "Deleting account: %s", d->account->name );
               log_string( log_buf );

               for( player = d->account->first_acc_char; player; player = player->next )
               {
                  sprintf( log_buf, "Deleting character: %s", player->name );
                  log_string( log_buf );

                  sprintf( character_buf, "%s%c/%s", PLAYER_DIR, tolower( player->name[0] ), player->name );
                  remove( character_buf );
               }

               ad_printf( d, "\n\rYour account has been deleted... Goodbye!\n\r" );
               sprintf( account_buf, "%s%c/%s", ACCOUNT_DIR, tolower( d->account->name[0] ), d->account->name );
               remove( account_buf );
               UNLINK( d->account, first_account, last_account, next, prev );
               STRFREE( d->account->name );
               DISPOSE( d->account );
               close_socket( d, FALSE );
               return;
            }
         }

         else if( nifty_is_name_prefix( cmdline, "password" ) )
         {
            ad_printf( d, "\n\rCurrent account password:\n\r" );
            d->connected = CON_ACCOUNT_GET_PASSWORD;
            return;
         }

         else if( nifty_is_name_prefix( cmdline, "list" ) )
         {
            display_account_chars( d );
            ad_printf( d, "\nAccount> &W" );
            return;
         }

         else if( nifty_is_name_prefix( cmdline, "menu" ) )
         {
            display_account_menu( d );
            ad_printf( d, "\nAccount> &W" );
            return;
         }

         else if( nifty_is_name_prefix( cmdline, "points" ) )
         {
            char point_buf[MAX_STRING_LENGTH];

            sprintf( point_buf, "\n\rYour account current has %d Account Points.\n\rAccount> &W", d->account->points );
            ad_printf( d, point_buf );
            return;
         }

         else if( nifty_is_name_prefix( cmdline, "who" ) )
         {
            if( !d->account->verified )
            {
               ad_printf( d, "\n\rAccount must be verified to use this function.\n\rAccount> &W" );
               return;
            }
            display_who( d );
            ad_printf( d, "\nAccount> &W" );
            return;
         }

         else if( nifty_is_name_prefix( cmdline, "quit" ) )
         {
            save_account( d->account );
            sprintf( log_buf, "Close account: %s", d->account->name );
            log_string( log_buf );
            ad_printf( d, "\n\rGoodbye...\n\r" );
            close_socket( d, FALSE );
            return;
         }

         else
         {
            ad_printf( d, "\n\rCommand not found. See 'menu' for a list of valid commands.\n\rAccount> &W" );
            return;
         }

         break;
   }
}


void account_password( DESCRIPTOR_DATA * d, char *cmdline )
{
   if( !d )
   {
      ad_printf( d, "No descriptor present! Disconnecting...\n\r" );
      close_socket( d, FALSE );
      return;
   }

   switch( d->connected )
   {
      case CON_ACCOUNT_GET_PASSWORD:
         if( strcmp( sha256_crypt( cmdline ), d->account->password ) )
         {
            ad_printf( d, "\n\rInvalid password. Returning to menu.\n\r" );
            display_account_menu( d );
            d->connected = CON_ACCOUNT_PENDING;
            ad_printf( d, "\n\rAccount> &W" );
            return;
         }

         ad_printf( d, "\n\rEnter new account password:\n\r" );
         d->connected = CON_ACCOUNT_PASSWORD_NEW;
         break;

      case CON_ACCOUNT_PASSWORD_NEW:
         if( strlen( cmdline ) < 5 )
         {
            ad_printf( d, "\n\rThe password must be at least 5 characters long.\n\rEnter new account password: " );
            return;
         }

         if( strchr( cmdline, '~' ) )
         {
            ad_printf( d, "\n\rPassword may not contain a tilde symbol.\n\rEnter new account password: " );
            return;
         }

         d->account->temppass = STRALLOC( sha256_crypt( cmdline ) );
         ad_printf( d, "\n\rConfirm new account password: " );
         d->connected = CON_ACCOUNT_PASSWORD_CONFIRM;
         break;

      case CON_ACCOUNT_PASSWORD_CONFIRM:
         if( strcmp( sha256_crypt( cmdline ), d->account->temppass ) )
         {
            ad_printf( d, "\n\rPasswords do not match. Failed to change password.\n\r" );
            display_account_menu( d );
            ad_printf( d, "Account> &W" );
            d->connected = CON_ACCOUNT_PENDING;
            STRFREE( d->account->temppass );
            return;
         }

         sprintf( log_buf, "%s changed their account password.", d->account->name );
         log_string( log_buf );
         d->account->password = STRALLOC( sha256_crypt( cmdline ) );
         STRFREE( d->account->temppass );
         ad_printf( d, "\n\rPassword changed successfully.\n\r" );
         save_account( d->account );
         display_account_menu( d );
         ad_printf( d, "\n\rAccount> &W" );
         d->connected = CON_ACCOUNT_PENDING;
         break;
   }
}


/*******************************************************************************
******************************* Account Commands *******************************
*******************************************************************************/

void do_account( CHAR_DATA * ch, char *argument )
{
   ACCOUNT_DATA *account;
   ACC_CHAR_DATA *accdata;
   int count = 0;

   if( IS_NPC( ch ) )
      return;

   account = ch->desc->account;

   if( !account )
   {
      send_to_char( "&RYou have no account!&D\n\r", ch );
      return;
   }

   ch_printf( ch, "&WAccount Details&b:&D\n\r" );
   ch_printf( ch, "&B---------------------------------------------------------------------&D\n\r" );
   ch_printf( ch, "&WAccount Name&b: &z%-20s &WAccount Type&b: &z%s&D\n\r", account->name, account->immortal ? "Immortal" : "Player" );
   ch_printf( ch, "&WCharacter   &b: &z%-20s &WMultiplay   &b: &z%s&D\n\r", ch->name, account->multiplay ? "Yes" : "No" );
   ch_printf( ch, "&WPoints      &b: &z%-5d                &WEmail       &b: &z%s&D\n\r", account->points, account->email );
   ch_printf( ch, "&B---------------------------------------------------------------------&D\n\r" );

   ch_printf( ch, "&WCharacter       Race            Clan                 Level   Quit Room&D\n\r" );

   for( accdata = account->first_acc_char; accdata; accdata = accdata->next )
   {
      ch_printf( ch, "&W%-15s %-15s %-20s %-5d   %-20s&D\n\r", accdata->name, npc_race[accdata->race], accdata->clan ? accdata->clan : "None", accdata->level, accdata->quit_location ? smash_color( accdata->quit_location ) : "None" );
      count++;
   }

   if( !count )
   {
      send_to_char( "&WNone.\n\r", ch );
      return;
   }

   ch_printf( ch, "&B---------------------------------------------------------------------&D\n\r" );
   ch_printf( ch, "\n\r&WYou have %d out of %d characters.&D\n\r", account->alts, account->maxalts );

   return;
}


void do_verify( CHAR_DATA * ch, char *argument )
{
   ACCOUNT_DATA *account;
   char email[MAX_STRING_LENGTH];
   char log[MAX_STRING_LENGTH];
   int verification = 0;

   if( IS_NPC( ch ) )
      return;

   account = ch->desc->account;

   if( !account )
   {
      send_to_char( "&RYou have no account!&D\n\r", ch );
      return;
   }

   if( NOT_AUTHED( ch ) )
   {
      send_to_char( "&RYou cannot verify your account until after you are authorized!&D\n\r", ch );
      return;
   }

   if( argument[0] == '\0' )
   {
      if( account->verified )
      {
         send_to_char( "&GYour account is already verified!\n\r", ch );
         return;
      }

      send_to_char( "&RSyntax: verify send\n\r", ch );
      send_to_char( "&R        verify <verification number>&D\n\r", ch );
      return;
   }

   if( !str_cmp( argument, "send" ) )
   {
      if( account->verified )
      {
         send_to_char( "&RYour account is already verified!\n\r", ch );
         return;
      }

      if( account->verify > 0 )
      {
         send_to_char( "&RIt seems we have already sent you an email verification.\n\r", ch );
         send_to_char( "&RIf not, please contact an Immortal.\n\r", ch );
         return;
      }

      if( !account->email )
      {
         send_to_char( "&RYour account does not have an email address!\n\r", ch );
         send_to_char( "&RUse 'email' command to set email address.\n\r", ch );
         return;
      }

      verification = number_range( 11111, 99999 );

      // Begin Email Function   - Thanks to Xerves for the send_mail SMAUG snippet
      static char sendstring[1000];
      FILE *fp = NULL;
      FILE *mfp = NULL;

      strcpy( sendstring, "" );
      sprintf( email, "%s%s.account", EMAIL_DIR, capitalize( account->name ) );

      fp = fopen( email, "w" );
      fprintf( fp, "Hello %s!\n\n", account->name );
      fprintf( fp, "While playing %s, you requested that we send\n", MUD_NAME );
      fprintf( fp, "you a verifcation code for your account! The verification code is:\n" );
      fprintf( fp, "\n%d\n\n", verification );
      fclose( fp );

      sprintf( sendstring, "%s -s \"Verification Email\" \"%s\" < %s", MAIL_ROOT_DIR, account->email, email );

      sprintf( log, "Account: %s (%s): Verification email sent to %s\n\r", account->name, ch->name, account->email );
      append_to_file( EMAIL_LOG, log );

      if( ( mfp = popen( sendstring, "w" ) ) == NULL )
      {
         send_to_char( "&RError. Mail function not found on this server! Inform an Immortal!\n\r", ch );
         bug( "Error. Mail function not found for account verification!", 0 );
         return;
      }
      pclose( mfp );
      // End Email Function

      account->verify = verification;
      save_account( account );

      ch_printf( ch, "&GA verification email has been sent to &W%s&D\n\r", account->email );
      send_to_char( "&GIf you do not receive an email with an hour, please contact an Immortal\n\r", ch );
      return;
   }

   if( account->verified )
   {
      send_to_char( "&RYour account is already verified!\n\r", ch );
      return;
   }

   if( !account->verify )
   {
      send_to_char( "&RYour account doesn't have a verification number... Please 'send' for one.\n\r", ch );
      return;
   }

   if( !isdigit( argument[0] ) )
   {
      send_to_char( "&RVerification must be a number!\n\r", ch );
      return;
   }

   if( atoi( argument ) != account->verify )
   {
      send_to_char( "&RThat is not the verification number that we sent you!\n\r", ch );
      return;
   }

   if( atoi( argument ) == account->verify )
   {
      account->verified = 1;
      if( account->maxalts == 1 )
         account->maxalts = 3;
      account->verify = 0;
      save_account( account );
      send_to_char( "&GYou have verified your account!\n\r", ch );
      sprintf( log_buf, "Account: %s (%s): Account has been verified.\n\r", account->name, ch->name );
      append_to_file( EMAIL_LOG, log_buf );
      log_string( log_buf );
      return;
   }

   return;
}