swrots/.slog/
swrots/doc/mudprogs/
swrots/gods/
swrots/html/profiles/
swrots/log/
swrots/planets/
swrots/player/
swrots/player/u/
/****************************************************************************
 *                   ^     +----- |  / ^     ^ |     | +-\                  *
 *                  / \    |      | /  |\   /| |     | |  \                 *
 *                 /   \   +---   |<   | \ / | |     | |  |                 *
 *                /-----\  |      | \  |  v  | |     | |  /                 *
 *               /       \ |      |  \ |     | +-----+ +-/                  *
 ****************************************************************************
 * AFKMud (c)1997-2002 Alsherok. Contributors: Samson, Dwip, Whir,          *
 * Cyberfox, Karangi, Rathian, Cam, Raine, and Tarl.                        *
 *                                                                          *
 * Original SMAUG 1.4a written by Thoric (Derek Snider) with Altrag,        *
 * Blodkai, Haus, Narn, Scryn, Swordbearer, Tricops, Gorog, Rennard,        *
 * Grishnakh, Fireblade, and Nivek.                                         *
 *                                                                          *
 * Original MERC 2.1 code by Hatchet, Furey, and Kahn.                      *
 *                                                                          *
 * Original DikuMUD code by: Hans Staerfeldt, Katja Nyboe, Tom Madsen,      *
 * Michael Seifert, and Sebastian Hammer.                                   *
 ****************************************************************************
 *                          Pfile Pruning Module                            *
 ****************************************************************************/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include "mud.h"

/* Globals */
time_t		    pfile_time;
HOUR_MIN_SEC  	    set_pfile_time_struct;
HOUR_MIN_SEC *      set_pfile_time;
struct tm *         new_pfile_time;
struct tm           new_pfile_struct;
time_t              new_pfile_time_t;
sh_int	num_pfiles; /* Count up number of pfiles */

void save_timedata( void )
{
    FILE *fp;
    char filename[MIL];
    
    sprintf( filename, "%stime.dat", SYSTEM_DIR );
    
    fclose( fpReserve );
    if ( ( fp = fopen( filename, "w" ) ) == NULL )
    {
        bug( "save_timedata: fopen" );
        perror( filename );
    }
    else
    {
      fprintf( fp, "#TIME\n" );
      fprintf( fp, "Purgetime %ld\n", new_pfile_time_t   );
      fprintf( fp, "End\n\n"						);
      fprintf( fp, "#END\n"						);
    }
    fclose( fp );
    fpReserve = fopen( NULL_FILE, "r" );
    return;
}

#ifdef KEY
#undef KEY
#endif
#define KEY( literal, field, value )                                    \
                                if ( !str_cmp( word, literal ) )        \
                                {                                       \
                                    field  = value;                     \
                                    fMatch = TRUE;                      \
                                    break;                              \
                                }

/* Reads the actual time file from disk - Samson 1-21-99 */
void fread_timedata( FILE *fp )
{
    char *word = NULL;
    bool fMatch = FALSE;
    
    for ( ; ; )
    {
        word   = feof( fp ) ? "End" : fread_word( fp );
        fMatch = FALSE;
        
        switch ( UPPER(word[0]) )
        {
          case '*':
            fMatch = TRUE;
            fread_to_eol( fp );
            break;

          case 'E':
            if ( !str_cmp( word, "End" ) )
                return;
            break;

	    case 'P':
		KEY( "Purgetime", new_pfile_time_t,		fread_number( fp ) );
		break;
        }

        if ( !fMatch )
        {
            bug( "Fread_timedata: no match: %s", word );
            fread_to_eol( fp );
        }
     }
}

bool load_timedata( void )
{
    char filename[MIL];
    FILE *fp;
    bool found;
    
    found = FALSE;
    sprintf( filename, "%stime.dat", SYSTEM_DIR );
    
    if ( ( fp = fopen( filename, "r" ) ) != NULL )
    {
        
        found = TRUE;
        for ( ; ; )
        {
            char letter = '\0';
            char *word = NULL;
            
            letter = fread_letter( fp );
            if ( letter == '*' )
            {
                fread_to_eol( fp );
                continue;
            }
            
            if ( letter != '#' )
            {
                bug( "Load_timedata: # not found." );
                break;
            }
            
            word = fread_word( fp );
            if ( !str_cmp( word, "TIME" ) )
            {
                fread_timedata( fp );
                break;
            }
            else
                if ( !str_cmp( word, "END"	) )
                    break;
                else
                {
                    bug( "Load_timedata: bad section - %s.", word );
                    break;
                }
        }
        FCLOSE( fp );
    }
    
    return found;
}

void init_pfile_scan_time( void )
{
    /*
     * Init pfile scan time.
     */
    set_pfile_time = &set_pfile_time_struct;
    
    new_pfile_time = update_time(localtime(&current_time));
    /* Copies *new_pfile_time to new_pfile_struct, and then points
       new_pfile_time to new_pfile_struct again. -- Alty */
    new_pfile_struct = *new_pfile_time;
    new_pfile_time = &new_pfile_struct;
    new_pfile_time->tm_mday += 1;
    if(new_pfile_time->tm_hour > 12)
    	new_pfile_time->tm_mday += 1;
    new_pfile_time->tm_sec = 0;
    new_pfile_time->tm_min = 0;
    new_pfile_time->tm_hour = 3;

    /* Update new_pfile_time (due to day increment) */
    new_pfile_time = update_time(new_pfile_time);
    new_pfile_struct = *new_pfile_time;
    new_pfile_time = &new_pfile_struct;
    /* Bug fix submitted by Gabe Yoder */
    new_pfile_time_t = mktime(new_pfile_time);
    /* check_pfiles(mktime(new_pfile_time)); */

    if( !load_timedata() )
    {
	strcpy( log_buf, "Pfile scan time reset to default time of 3am." );
	log_string( log_buf );
    }
    return;
}

time_t	now_time;
sh_int	deleted = 0;
sh_int   days = 0;

#if defined(KEY)
#undef KEY
#endif

#define KEY( literal, field, value )					\
				if ( !strcmp( word, literal ) )	\
				{					\
				    field  = value;			\
				    fMatch = TRUE;			\
				    break;				\
				}

void fread_pfile( FILE *fp, time_t tdiff, char *fname, bool count )
{
   char *word;
   char *name = NULL;
   char *clan = NULL;
   char *deity = NULL;
   sh_int toplevel = 0;
   sh_int file_ver = 0;
   int pact;
   bool fMatch;

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

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

	   case 'A':
		KEY( "Act2",			pact,		fread_number( fp ) );
		break;

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

	   case 'D':
	      KEY( "Deity",		deity,	fread_string( fp ) );
	      break;

         case 'E':
	      if ( !strcmp( word, "End" ) )
	        goto timecheck;
	      break;

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

	   case 'T':
	      KEY( "Toplevel",		toplevel,	fread_number( fp ) );
	      break;

	   case 'V':
	      KEY( "Version",	file_ver,	fread_number( fp ) );
	      break;
	}

	if ( !fMatch )
	   fread_to_eol( fp );
   }

timecheck:

   if( count == FALSE && !IS_SET( pact, ACT_EXEMPT ) )
   {
      if ( toplevel < 5 && tdiff > sysdata.newbie_purge )
      {
         if ( unlink(fname) == -1 )
	      perror( "Unlink" );
         else
         {
            days = sysdata.newbie_purge;
            sprintf( log_buf, "Player %s was deleted. Exceeded time limit of %d days.", name, days );
            log_string( log_buf );
#ifdef AUTO_AUTH
	      remove_from_auth( name );
#endif
	      deleted++;
	      return;
         }
      }

      if ( toplevel < LEVEL_IMMORTAL && tdiff > sysdata.regular_purge )
      {
         if ( toplevel < LEVEL_IMMORTAL )
         {
            if ( unlink(fname) == -1 )
	         perror( "Unlink" );
            else
            {
	         days = sysdata.regular_purge;
               sprintf( log_buf, "Player %s was deleted. Exceeded time limit of %d days.", name, days );
               log_string( log_buf );
#ifdef AUTO_AUTH
	         remove_from_auth( name );
#endif
	         deleted++;
               return;
            }
         }
      }
   }

   if( clan != NULL )
   {
	CLAN_DATA *guild = get_clan( clan );
	char clanmemberlist[MAX_STRING_LENGTH];
	char buf[MAX_STRING_LENGTH];

	if( !guild )
	   return;
	
	if(guild->shortname[0] != '\0' && toplevel < LEVEL_IMMORTAL)
	{
	 sprintf(clanmemberlist, "%s%s.list", CLAN_DIR, guild->shortname);
	 sprintf(buf, "%s~", name);
	 append_to_file(clanmemberlist, buf);
	}


	guild->members++;
	save_clan( guild );
   }

   return;
}

void read_pfile( char *dirname, char *filename, bool count )
{
  FILE *fp;
  char fname[MSL];
  struct stat fst;
  time_t tdiff;

  now_time = time(0);

  sprintf( fname, "%s/%s", dirname, filename );

  if( !str_cmp(filename, ".clone") || !str_cmp(filename, ".home"))
    return;

  if ( stat( fname, &fst ) != -1 )
  {
    tdiff = (now_time - fst.st_mtime) / 86400;

    if ( ( fp = fopen ( fname, "r" ) ) != NULL )
    {
      for ( ; ; )
	{
	    char letter;
	    char *word;

	    letter = fread_letter( fp );
	    
	    if ( ( letter != '#' ) && ( !feof( fp ) ) )
             continue;

	    word = feof( fp ) ? "End" : fread_word( fp );

          if ( !str_cmp( word, "End" ) )
		break;

	    if ( !str_cmp( word, "PLAYER" ) )
	    	fread_pfile( fp, tdiff, fname, count );
	    else
	    if ( !str_cmp( word, "END" ) )	/* Done		*/
		break;
	}
	FCLOSE( fp );
    }
  }
  return;
}

void pfile_scan( bool count )
{
  DIR *dp;
  struct dirent *dentry;
  CLAN_DATA *clan;
  int len;
  char directory_name[100];
  char buf[MAX_STRING_LENGTH];

  sh_int alpha_loop;
  sh_int cou = 0;
  deleted = 0;

  now_time = time(0);
  nice(20);

   /* Reset all clans to 0 members prior to scan - Samson 7-26-00 */
   for( clan = first_clan; clan; clan = clan->next )
   {
	clan->members = 0;
	sprintf(buf, "%s%s.list", CLAN_DIR,clan->shortname);
        remove(buf);
   }

  for ( alpha_loop = 0; alpha_loop <= 25; alpha_loop++ )
  {
      sprintf( directory_name, "%s%c", PLAYER_DIR, 'a' + alpha_loop );
      /* log_string( directory_name ); */
      dp = opendir( directory_name );
      dentry = readdir( dp );
      while ( dentry )
      {
      	 len = strlen(dentry->d_name);
         if ( dentry->d_name[0] != '.' && str_cmp(dentry->d_name, ".clone") && str_cmp(dentry->d_name, ".home"))
         {
	    read_pfile( directory_name, dentry->d_name, count );
            cou++;
          }
         dentry = readdir( dp );
      }
      closedir( dp );
  }

  if( !count )
    log_string( "Pfile cleanup completed." );
  else
    log_string( "Pfile count completed." );
  
  sprintf( log_buf, "Total pfiles scanned: %d", cou );
  log_string( log_buf );

  if( !count )
  {
    sprintf( log_buf, "Total pfiles deleted: %d", deleted );
    log_string( log_buf );
 
    sprintf( log_buf, "Total pfiles remaining: %d", cou - deleted );
    num_pfiles = cou - deleted;
    log_string( log_buf );
  }
  else
    num_pfiles = cou;

  return;
}

void do_pfiles( CHAR_DATA *ch, char *argument )
{
   char buf[MSL];

   if ( IS_NPC(ch) )
   {
	send_to_char( "Mobs cannot use this command!\n\r", ch );
	return;
   }

   if ( argument[0] == '\0' || !argument )
   {
     /* Makes a backup copy of existing pfiles just in case - Samson */
     sprintf( buf, "tar -cf %spfiles.tar %s*", PLAYER_DIR, PLAYER_DIR );

     /* GAH, the shell pipe won't process the command that gets pieced
	  together in the preceeding lines! God only knows why. - Samson */
     system( buf );

     sprintf( log_buf, "Manual pfile cleanup started by %s.", ch->name );
     log_string( log_buf );
     pfile_scan( FALSE );
#ifdef SAMSONRENT
     rent_update();
#endif
     return;
   }

   if ( !str_cmp( argument, "settime" ) )
   {
      new_pfile_time_t = current_time + 86400;
      save_timedata( );
	send_to_char( "New cleanup time set for 24 hrs from now.\n\r", ch );
	return;
   }

   if ( !str_cmp( argument, "count" ) )
   {
	sprintf( log_buf, "Pfile count started by %s.", ch->name );
	log_string( log_buf );
	pfile_scan( TRUE );
      return;
   }

   send_to_char( "Invalid argument.\n\r", ch );
   return;
}

void check_pfiles( time_t reset )
{
  /* This only counts them up on reboot if the cleanup isn't needed - Samson 1-2-00 */
  if ( reset == 255 && new_pfile_time_t > current_time )
  {
	reset = 0; /* Call me paranoid, but it might be meaningful later on */
     	log_string( "Counting pfiles....." );
 	pfile_scan( TRUE );
	return;
  }

  if ( new_pfile_time_t <= current_time )
  {
     if( sysdata.CLEANPFILES == TRUE )
     {

       char buf[MSL];

       /* Makes a backup copy of existing pfiles just in case - Samson */
       sprintf( buf, "tar -cf %spfiles.tar %s*", PLAYER_DIR, PLAYER_DIR );

       /* Would use the shell pipe for this, but alas, it requires a ch in order
	 to work, this also gets called during boot_db before the rare item
	 checks for the rent code - Samson */
       system( buf ); 

       new_pfile_time_t = current_time + 86400;
       save_timedata( );
       log_string( "Automated pfile cleanup beginning...." );
       pfile_scan( FALSE );
#ifdef SAMSONRENT
	 if( reset == 0 )
	    rent_update();
#endif
     }
     else
     {
       new_pfile_time_t = current_time + 86400;
       save_timedata( );
	 log_string( "Counting pfiles....." );
	 pfile_scan( TRUE );
#ifdef SAMSONRENT
	 if( reset == 0 )
	    rent_update();
#endif
     }
  }
  return;
}

void do_exempt(CHAR_DATA *ch, char *argument)
{
 CHAR_DATA *victim;

 if(argument[0] == '\0')
 {
   send_to_char("Syntax: exempt <char>\n\r", ch);
   return;
 }

 if((victim = get_char_world(ch, argument)) == NULL)
 {
   send_to_char("They must be online to exempt them.\n\r", ch);
   return;
 }

 if(IS_NPC(victim))
 {
   send_to_char("You can only exempt mobs.\n\r", ch);
   return;
 }

  SET_BIT(victim->pcdata->act2, ACT_EXEMPT); 

 send_to_char("You have been exempt from deletion.\n\r", victim);
 send_to_char("They have been exempt from deletion.\n\r", ch);

return;
}

void add_member(char *name, char *shortname)
{
  char buf[MAX_STRING_LENGTH];
  char fbuf[MAX_STRING_LENGTH];

 if(name[0] == '\0' || !name)
 {
   bug("add_member: No name!\n\r");
   return;
 }

 if(shortname[0] == '\0' || !shortname)
 {
   bug("add_member: No shortname!\n\r");
   return;
 }
 
 sprintf(fbuf, "%s%s.list", CLAN_DIR, shortname);
 sprintf(buf, "%s~", name);
 append_to_file(fbuf, buf);

}

void remove_member(char *name, char *shortname)
{
 FILE *fpList;
 FILE *fpNew;
 char *buf;
 char list[MAX_STRING_LENGTH];
 char temp[MAX_STRING_LENGTH];

 if(name[0] == '\0')
 {
   bug("remove_member: No name!\n\r");
   return;
 }

 if(shortname[0] == '\0' || !shortname)
 {
   bug("remove_member: No shortname!\n\r");
   return;
 }
 
 sprintf(list, "%s%s.list", CLAN_DIR, shortname);
 sprintf(temp,"%s.temp", list);

 if ( ( fpList = fopen(list, "r" ) ) == NULL )
 {
    bug("Unable to open member list");
    return;
 }

 if ( ( fpNew = fopen(temp, "w" ) ) == NULL)
 {
    bug("remove_member: Unable to write temp list");
    return;
 }
 
 for ( ; ; )
 {
     if(feof(fpList))
	break;
     buf = feof( fpList ) ? "End" : fread_string( fpList );
     if(!str_cmp(buf, "End") || buf[0] == '\0')
	break;
     if(str_cmp(name, buf) && strlen(buf) > 2)
	fprintf(fpNew, "%s~\n", buf);
 }
 fclose( fpNew );
 fclose( fpList );
 rename(temp, list);
}