talker/
talker/bin/
talker/files/whois/
talker/update/
talker/update/bin/
/*
 * Plists.c
 */

#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>
#include <signal.h>
#include <setjmp.h>

#include "fix.h"
#include "config.h"
#include "player.h"

/* externs */

extern int      restore_player_title(player *, char *, char *);
extern void     decompress_list(saved_player *);
extern char    *end_string(), *retrieve_room_data(),
               *retrieve_list_data(), *retrieve_mail_data(),
	       *retrieve_alias_data(), *retrieve_item_data();
extern void     construct_room_save(), construct_list_save(), 
		construct_mail_save(), construct_alias_save(), 
		construct_item_save();
extern void     decompress_room(room *);
extern void     decompress_alias(saved_player *);
extern void     decompress_item(saved_player *);
extern void     log(char *, char *);
extern void     handle_error(char *);
extern void     free_room_data(saved_player *);
extern void     players_update_function(player *p);
extern void     initialise_data(player *p);
extern char    *get_int(int *dest, char *source);
extern char    *get_string(char *dest, char *source);
extern char    *store_string(char *dest, char *source);
extern char    *store_int(char *dest, int source);
extern void     extra_save_data(player *p);
extern void     extra_load_data(player *p, char *r);
/* interns */

void            error_on_load();
int             bad_player_load = 0;
char            player_loading[MAX_NAME + 2];
jmp_buf         jmp_env;

saved_player  **saved_hash[26];
int             update[26];

void            save_player();
int             restore_player();


void            do_update()
{
  player         *p;
  saved_player   *scan, **hash;
  int             i, j, fd;
  
  fd = open("/dev/null", O_WRONLY);
  p = (player *) MALLOC(sizeof(player));
  for (j = 0; j < 26; j++)
  {
    hash = saved_hash[j];
    for (i = 0; i < HASH_SIZE; i++, hash++)
    {
      for (scan = *hash; scan; scan = scan->next)
      {
        if (scan->residency != STANDARD_ROOMS
            && scan->residency != SYSTEM_ROOM)
	  /*            &&(scan->residency != BANISHED) && (scan->residency != BANISHD))*/
        {
          memset((char *) p, 0, sizeof(player));
          p->fd = fd;
          p->script = 0;
          p->location = (room *) - 1;
          restore_player(p, scan->lower_name);
          save_player(p);
        }
      }
    }
  }
  close(fd);
}

/* saved player stuff */

/* see if a saved player exists (given lower case name) */

saved_player   *find_saved_player(char *name)
{
  saved_player  **hash, *list;
   int             sum = 0,h;
   char           *c;
   if (!isalpha(*name))
      return 0;
   hash = saved_hash[((int) (tolower(*name)) - (int) 'a')];
   for (c = name; *c; c++)
   {
      if (isalpha(*c))
         sum += (int) (tolower(*c)) - 'a';
      else
    return 0;
   }
   list = *(hash + (sum % HASH_SIZE));
   for (; list; list = list->next)
      if (!strcmp(name, list->lower_name))
         return list;
   return 0;
}


/* hard load and save stuff (ie to disk and back) */

int             load_player(player * p)
{
   saved_player   *sp;
   char           *r;

   lower_case(p->lower_name);
   sp = find_saved_player(p->lower_name);
   p->saved = sp;
   if (!sp)
      return 0;

   p->residency = sp->residency;

   p->saved_residency = p->residency;
      p->system_flags = sp->system_flags;
      p->tag_flags = sp->tag_flags;
      p->custom_flags = sp->custom_flags;
      p->misc_flags = sp->misc_flags;
      p->pennies = sp->pennies;

   if (sp->residency == BANISHED || sp->residency == STANDARD_ROOMS
        || sp->residency == SYSTEM_ROOM
        || sp->residency == BANISHD)
      return 1;

   r = sp->data.where;
   r = get_string(p->name, r);
   r = get_string(p->prompt, r);
   r = get_string(p->converse_prompt, r);
   r = get_string(p->email, r);
   r = get_string(p->password, r);
   r = get_string(p->title, r);
   r = get_string(p->plan, r);
   r = get_string(p->description, r);
   r = get_string(p->enter_msg, r);
   r = get_string(p->pretitle, r);
   r = get_string(p->ignore_msg, r);
   r = get_string(p->room_connect, r);
   r = get_int(&p->term_width, r);
   r = get_int(&p->word_wrap, r);
   r = get_int(&p->max_rooms, r);
   r = get_int(&p->max_exits, r);
   r = get_int(&p->max_autos, r);
   r = get_int(&p->max_list, r);
   r = get_int(&p->max_mail, r);
   r = get_int(&p->gender, r);
   r = get_int(&p->no_shout, r);
   r = get_int(&p->total_login, r);
   r = get_int(&p->term, r);
   r = get_int(&p->birthday, r);
   r = get_int(&p->age, r);
   r = get_int(&p->jetlag, r);
   r = get_int(&p->sneezed, r);
        r = get_string(p->logonmsg, r);
        r = get_string(p->logoffmsg, r);
        r = get_string(p->blockmsg, r);
        r = get_string(p->exitmsg, r);
        r = get_int(&p->time_in_main, r);
        r = get_int(&p->no_sing, r);
        r = get_string(p->married_to, r);
        r = get_string(p->irl_name, r);
        r = get_string(p->alt_email, r);
        r = get_string(p->hometown, r);
        r = get_string(p->spod_class, r);
        r = get_string(p->favorite1, r);
        r = get_string(p->favorite2, r);
        r = get_string(p->favorite3, r);
        r = get_int(&p->total_idle_time, r);
        r = get_int(&p->max_alias, r);
        r = get_string(p->colorset, r);
        r = get_string(p->ressied_by, r);
        r = get_string(p->git_string, r);
        r = get_string(p->git_by, r);
        r = get_int(&p->warn_count, r);
        r = get_int(&p->eject_count, r);
        r = get_int(&p->idled_out_count, r);
        r = get_int(&p->booted_count, r);
        r = get_int(&p->num_ressied, r);
        r = get_int(&p->num_warned, r);
        r = get_int(&p->num_ejected, r);
        r = get_int(&p->num_rmd, r);
        r = get_int(&p->num_booted, r);
        r = get_int(&p->first_login_date, r);
        r = get_int(&p->max_items, r);
        r = get_int(&p->prs_record, r);
        r = get_string(p->ingredients, r);


   extra_load_data(p, r);

   decompress_list(sp);
   decompress_alias(sp);
   decompress_item(sp);
      p->system_flags = sp->system_flags;
      p->tag_flags = sp->tag_flags;
      p->custom_flags = sp->custom_flags;
      p->misc_flags = sp->misc_flags;
      p->pennies = sp->pennies;

   
   return 1;
}

file            construct_save_data(player * p)
{
   file            d;
   d.where = stack;

   /* Initialise any new variables */
   initialise_data(p); 
   players_update_function(p);
   stack = store_string(stack, p->name);
   stack = store_string(stack, p->prompt);
   stack = store_string(stack, p->converse_prompt);
   stack = store_string(stack, p->email);
   if (p->password[0] == -1)
      p->password[0] = 0;
   stack = store_string(stack, p->password);
   stack = store_string(stack, p->title);
   stack = store_string(stack, p->plan);
   stack = store_string(stack, p->description);
   stack = store_string(stack, p->enter_msg);
   stack = store_string(stack, p->pretitle);
   stack = store_string(stack, p->ignore_msg);
   stack = store_string(stack, p->room_connect);
   stack = store_int(stack, p->term_width);
   stack = store_int(stack, p->word_wrap);
   stack = store_int(stack, p->max_rooms);
   stack = store_int(stack, p->max_exits);
   stack = store_int(stack, p->max_autos);
   stack = store_int(stack, p->max_list);
   stack = store_int(stack, p->max_mail);
   stack = store_int(stack, p->gender);
   stack = store_int(stack, p->no_shout);
   stack = store_int(stack, p->total_login);
   stack = store_int(stack, p->term);
   stack = store_int(stack, p->birthday);
   stack = store_int(stack, p->age);
   stack = store_int(stack, p->jetlag);
   stack = store_int(stack, p->sneezed);
        stack = store_string(stack, p->logonmsg);
        stack = store_string(stack, p->logoffmsg);
        stack = store_string(stack, p->blockmsg);
        stack = store_string(stack, p->exitmsg);
        stack = store_int(stack, p->time_in_main);
        stack = store_int(stack, p->no_sing);
        stack = store_string(stack, p->married_to);
        stack = store_string(stack, p->irl_name);
        stack = store_string(stack, p->alt_email);
        stack = store_string(stack, p->hometown);
        stack = store_string(stack, p->spod_class);
        stack = store_string(stack, p->favorite1);
        stack = store_string(stack, p->favorite2);
        stack = store_string(stack, p->favorite3);
        stack = store_int(stack, p->total_idle_time);
        stack = store_int(stack, p->max_alias);
        stack = store_string(stack, p->colorset);
        stack = store_string(stack, p->ressied_by);
        stack = store_string(stack, p->git_string);
        stack = store_string(stack, p->git_by);
        stack = store_int(stack, p->warn_count);
        stack = store_int(stack, p->eject_count);
        stack = store_int(stack, p->idled_out_count);
        stack = store_int(stack, p->booted_count);
        stack = store_int(stack, p->num_ressied);
        stack = store_int(stack, p->num_warned);
        stack = store_int(stack, p->num_ejected);
        stack = store_int(stack, p->num_rmd);
        stack = store_int(stack, p->num_booted);
        stack = store_int(stack, p->first_login_date);
        stack = store_int(stack, p->max_items);
        stack = store_int(stack, p->prs_record);
        stack = store_string(stack, p->ingredients);

   
   extra_save_data(p);
   
   d.length = (int) stack - (int) d.where;
   stack = d.where;
   return d;
}
/* extract one player */

void           extract_player(char *where, int length)
{
   int             len, sum;
   char           *oldstack, *c;
   saved_player   *old, *sp, **hash;

   oldstack = stack;
   where = get_int(&len, where);
   where = get_string(oldstack, where);
   stack = end_string(oldstack);
   old = find_saved_player(oldstack);
   sp = old;
   if (!old)
   {
      sp = (saved_player *) MALLOC(sizeof(saved_player));
      memset((char *) sp, 0, sizeof(saved_player));
      strncpy(sp->lower_name, oldstack, MAX_NAME);
      strncpy(player_loading, sp->lower_name, MAX_NAME);
      sp->rooms = 0;
      sp->mail_sent = 0;
      sp->mail_received = 0;
      sp->list_top = 0;
      hash = saved_hash[((int) sp->lower_name[0] - (int) 'a')];
      for (sum = 0, c = sp->lower_name; *c; c++)
         sum += (int) (*c) - 'a';
      hash = (hash + (sum % HASH_SIZE));
      sp->next = *hash;
      *hash = sp;
   }
   where = get_int(&sp->last_on, where);
   where = get_int(&sp->system_flags, where);
   where = get_int(&sp->tag_flags, where);
   where = get_int(&sp->custom_flags, where);
   where = get_int(&sp->misc_flags, where);
   where = get_int(&sp->pennies, where);
   where = get_int(&sp->residency, where);
 
   if (sp->residency == BANISHED)
      sp->residency = BANISHD;
   if (sp->residency == BANISHD)
   {
      sp->last_host[0] = 0;
      sp->data.where = 0;
      sp->data.length = 0;
      stack = oldstack;
      return;
   }
/* PUT ANYTHING TO CHANGE RESIDENCY OR OTHER FLAGS HERE */
   where = get_string(sp->last_host, where);
   where = get_string(sp->email, where);
   where = get_int(&sp->data.length, where);
   sp->data.where = (char *) MALLOC(sp->data.length);
   memcpy(sp->data.where, where, sp->data.length);
   where += sp->data.length;
   where = retrieve_room_data(sp, where);
   where = retrieve_list_data(sp, where);
   where = retrieve_alias_data(sp, where);
   where = retrieve_item_data(sp, where);
   where = retrieve_mail_data(sp, where);
   stack = oldstack;
}

/* hard load in on player file */

void            hard_load_one_file(char c)
{
   char           *oldstack, *where, *scan;
   int             fd, length, len2, i, fromjmp;

   oldstack = stack;
   if (sys_flags & VERBOSE)
   {
      sprintf(oldstack, "Loading player file '%c'.", c);
      stack = end_string(oldstack);
      log("boot", oldstack);
      stack = oldstack;
   }
#ifdef PC
   sprintf(oldstack, "files\\players\\%c", c);
   fd = open(oldstack, O_RDONLY | O_BINARY);
#else
   sprintf(oldstack, "%s%c", rc_options->pfile_path,c);
   fd = open(oldstack, O_RDONLY | O_NDELAY);
#endif
   if (fd < 0)
   {
      sprintf(oldstack, "Failed to load player file '%c'", c);
      stack = end_string(oldstack);
      log("error", oldstack);
   } else
   {
      length = lseek(fd, 0, SEEK_END);
      lseek(fd, 0, SEEK_SET);
      if (length)
      {
         where = (char *) MALLOC(length);
         if (read(fd, where, length) < 0)
            handle_error("Can't read player file.");
         for (i = 0, scan = where; i < length;)
         {
            get_int(&len2, scan);
            fromjmp = setjmp(jmp_env);
            if (!fromjmp && !bad_player_load)
            {
               extract_player(scan, len2);
            } else
            {
               sprintf(oldstack, "Bad Player \'%s\' deleted on load.",
                       player_loading);
               stack = end_string(oldstack);
               log("boot", oldstack);
               stack = oldstack;
               remove_player_file(player_loading);
               bad_player_load = 0;
            }
            i += len2;
            scan += len2;
         }
         FREE(where);
      }
      close(fd);
   }
   stack = oldstack;
}


/* load in all the player files */

void            hard_load_files()
{
  char            c;
  int             i, hash_length;
  char           *oldstack;
  
  oldstack = stack;
  hash_length = HASH_SIZE * sizeof(saved_player *);
  for (i = 0; i < 26; i++)
  {
    saved_hash[i] = (saved_player **) MALLOC(hash_length);
    memset((void *) saved_hash[i], 0, hash_length);
  }
  for (c = 'a'; c <= 'z'; c++)
    hard_load_one_file(c);
}


/* write one player file out */

void            write_to_file(saved_player * sp)
{
   char           *oldstack;
   int             length;
#ifdef PLIST_TRACK
   printf("write_to_file:%s\n", sp->lower_name);
#endif
   oldstack = stack;
   if (sys_flags & VERBOSE && sys_flags & PANIC)
   {
      sprintf(oldstack, "Attempting to write player '%s'.", sp->lower_name);
      stack = end_string(oldstack);
      log("sync", oldstack);
      stack = oldstack;
   }
   stack += 4;
   stack = store_string(stack, sp->lower_name);
   stack = store_int(stack, sp->last_on);
   stack = store_int(stack, sp->system_flags);
   stack = store_int(stack, sp->tag_flags);
   stack = store_int(stack, sp->custom_flags);
   stack = store_int(stack, sp->misc_flags);
   stack = store_int(stack, sp->pennies);
   stack = store_int(stack, sp->residency);

   if ((sp->residency != BANISHED) )
   {
      stack = store_string(stack, sp->last_host);
      stack = store_string(stack, sp->email);
      stack = store_int(stack, sp->data.length);
      memcpy(stack, sp->data.where, sp->data.length);
      stack += sp->data.length;
      construct_room_save(sp);
      construct_list_save(sp);
      construct_alias_save(sp);
      construct_item_save(sp);
      construct_mail_save(sp);
    }
   length = (int) stack - (int) oldstack;
   (void) store_int(oldstack, length);

}

/* sync player files corresponding to one letter */

void            sync_to_file(char c, int background)
{
  saved_player   *scan, **hash;
   char           *oldstack;
   int             fd, i, length;
#ifdef PLIST_TRACK_TEST
  printf("sync_to_file(%c)\n", c);
#endif
   if (background && fork())
      return;

   oldstack = stack;
   if (sys_flags & VERBOSE)
   {
      sprintf(oldstack, "Syncing File '%c'.", c);
      stack = end_string(oldstack);
      log("sync", oldstack);
      stack = oldstack;
   }
   hash = saved_hash[((int) c - (int) 'a')];
   for (i = 0; i < HASH_SIZE; i++, hash++)
      for (scan = *hash; scan; scan = scan->next)
    if (scan->residency != STANDARD_ROOMS
        && scan->residency != SYSTEM_ROOM)
       write_to_file(scan);
   length = (int) stack - (int) oldstack;


   /* test that you can write out a file ok */

   sprintf(stack, "%sbackup_write", rc_options->new_pfile_path);
   fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
   if (fd < 0)
      handle_error("Primary open failed (player back)");
   if (write(fd, oldstack, length) < 0)
      handle_error("Primary write failed "
         "(playerback)");
   close(fd);
   sprintf(stack, "%s%c", rc_options->new_pfile_path, c);
   fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
   if (fd < 0)
      handle_error("Failed to open player file.");
   if (write(fd, oldstack, length) < 0)
      handle_error("Failed to write player file.");
   close(fd);
  update[(int) c - (int) 'a'] = 0;
   stack = oldstack;

   if (background)
      exit(0);

}

/* sync everything to disk */

void            sync_all()
{
   char            c, *oldstack;
   oldstack = stack;
   for (c = 'a'; c <= 'z'; c++)
      sync_to_file(c, 0);
}

/* flicks on the update flag for a particular player hash */

void            set_update(char c)
{
   update[(int) c - (int) 'a'] = 1;
}


/* removes an entry from the saved player lists */

int             remove_player_file(char *name)
{
  saved_player   *previous = 0, **hash, *list;
   char           *c;
   int             sum = 0;

   if (!isalpha(*name))
   {
      log("error", "Tried to remove non-player from save files.");
      return 0;
   }
   strcpy(stack, name);
   lower_case(stack);

   hash = saved_hash[((int) (*stack) - (int) 'a')];
   for (c = stack; *c; c++)
   {
      if (isalpha(*c))
         sum += (int) (*c) - 'a';
      else
      {
         log("error", "Remove bad name from save files");
         return 0;
      }
   }

   hash += (sum % HASH_SIZE);
   list = *hash;
   for (; list; previous = list, list = list->next)
   {
      if (!strcmp(stack, list->lower_name))
      {
         if (previous)
            previous->next = list->next;
         else
            *hash = list->next;
         if (list->data.where)
            FREE(list->data.where);
         if (list->mail_received)
            FREE(list->mail_received);
         free_room_data(list);
         FREE((void *) list);
         set_update(*stack);
         return 1;
      }
   }
   return 0;
}

/* the routine that sets everything up for the save */

void            save_player(player * p)
{
  saved_player   *old, **hash, *sp;
  int             sum;
  file            data;
  char           *c, *oldstack;
  int verb = 1;
  oldstack = stack;
  
  if (!(p->location) || !(p->name[0])
      || p->residency == NON_RESIDENT)
    return;
  
  if (sys_flags & PANIC)
  {
    c = stack;
    sprintf(c, "Attempting to save player %s.", p->name);
    stack = end_string(c);
    log("boot", c);
    stack = c;
  }
  if (!(isalpha(p->lower_name[0])))
  {
    log("error", "Tried to save non-player.");
    return;
  }
  if (p->residency & SYSTEM_ROOM)
    verb = 0;
  if (verb)
  {
    if (!(p->password[0] && p->password[0] != -1))
    {
      p->residency |= NO_SYNC;
      stack = oldstack;
      return;
    }
    if (p->email[0] == 2)
    {
      p->residency |= NO_SYNC;
      stack = oldstack;
      return;
    }
  }
  p->residency &= ~NO_SYNC;
  p->saved_residency = p->residency;
  old = p->saved;
  sp = old;
  if (!old)
  {
    sp = (saved_player *) MALLOC(sizeof(saved_player));
    memset((char *) sp, 0, sizeof(saved_player));
    strncpy(sp->lower_name, p->lower_name, MAX_NAME);
    sp->rooms = 0;
    sp->mail_sent = 0;
    sp->mail_received = 0;
    sp->list_top = 0;
    hash = saved_hash[((int) p->lower_name[0] - (int) 'a')];
    for (sum = 0, c = p->lower_name; *c; c++)
    {
      if (isalpha(*c))
	sum += (int) (*c) - 'a';
      else
      {
	FREE(sp);
	return;
      }
    }
    hash = (hash + (sum % HASH_SIZE));
    sp->next = *hash;
    *hash = sp;
    p->saved = sp;
      sp->system_flags = p->system_flags;
      sp->tag_flags = p->tag_flags;
      sp->custom_flags = p->custom_flags;
      sp->misc_flags = p->misc_flags;
      sp->pennies = p->pennies;
    create_room(p);
  }
  data = construct_save_data(p);
  if (!data.length)
  {
    log("error", "Bad construct save.");
    return;
  }
  if (old && sp->data.where)
    FREE((void *) sp->data.where);
  sp->data.where = (char *) MALLOC(data.length);
  sp->data.length = data.length;
  memcpy(sp->data.where, data.where, data.length);
  sp->residency = p->saved_residency;
      sp->system_flags = p->system_flags;
      sp->tag_flags = p->tag_flags;
      sp->custom_flags = p->custom_flags;
      sp->misc_flags = p->misc_flags;
      sp->pennies = p->pennies;

  set_update(*(sp->lower_name));
  p->saved = sp;
}


/* load from a saved player into a current player */


/* load and do linking */

int             restore_player(player * p, char *name)
{
   return restore_player_title(p, name, 0);
}

int             restore_player_title(player * p, char *name, char *title)
{
   int             did_load;
   int             found_lower;
   char           *n;

   strncpy(p->name, name, MAX_NAME - 2);
   strncpy(p->lower_name, name, MAX_NAME - 2);
   lower_case(p->lower_name);
   if (!strcmp(p->name, p->lower_name))
     p->name[0] = toupper(p->name[0]);
   found_lower = 0;
   n = p->name;
   while (*n)
   {
     if (*n >= 'a' && *n <= 'z')
     {
       found_lower = 1;
     }
     *n++;
   }
   if (!found_lower)
   {
     n = p->name;
     *n++;
     while (*n)
     {
       *n = *n - ('A' - 'a');
       *n++;
     }
   }
   did_load = load_player(p);
   strcpy(p->assisted_by, "");
   if (title && *title)
   {
      strncpy(p->title, title, MAX_TITLE);
      p->title[MAX_TITLE] = 0;
   }
   if ((p->system_flags & IAC_GA_ON) && (!(p->flags & EOR_ON)))
      p->flags |= IAC_GA_DO;
   else
      p->flags &= ~IAC_GA_DO;
   if (p->residency == 0 && did_load == 1)
      p->residency = SYSTEM_ROOM;
   if (p->system_flags & SAVEDFROGGED)
      p->flags |= FROGGED;
   if (p->residency & PSU)
      p->no_shout = 0;
   p->saved_residency = p->residency;
   if ((p->word_wrap) > ((p->term_width) >> 1) || p->word_wrap < 0)
      p->word_wrap = (p->term_width) >> 1;
   if (p->term > 9)
      p->term = 0;
   return did_load;
}

/* init everything needed for the plist file */

void            init_plist()
{
   char           *oldstack;
   int             i;

   oldstack = stack;
   hard_load_files();
   for (i = 0; i < 26; i++)
      update[i] = 1;
   stack = oldstack;
}
/* Catch SEGV's and BUS's on load of players, hopefully... */

void error_on_load()
{
   bad_player_load = 1;
   longjmp(jmp_env, 0);
   longjmp(jmp_env, 0);
}