/***************************************************************************
*                           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;
}