pdirt/data/
pdirt/data/HELP/
pdirt/data/HELP/0/
pdirt/data/HELP/F/
pdirt/data/HELP/G/
pdirt/data/HELP/H/
pdirt/data/HELP/J/
pdirt/data/HELP/K/
pdirt/data/HELP/O/
pdirt/data/HELP/Q/
pdirt/data/HELP/R/
pdirt/data/HELP/U/
pdirt/data/HELP/V/
pdirt/data/HELP/Y/
pdirt/data/HELP/Z/
pdirt/data/MESSAGES/
pdirt/data/POWERINFO/
pdirt/data/WIZ_ZONES/
pdirt/drv/
pdirt/drv/bin/
pdirt/drv/compiler/converter/
pdirt/drv/compiler/libs/
pdirt/drv/compiler/scripts/
pdirt/drv/include/AberChat/
pdirt/drv/include/InterMud/
pdirt/drv/include/machine/
pdirt/drv/src/InterMud/
pdirt/drv/src/Players/
pdirt/drv/utils/UAFPort/
pdirt/drv/utils/dnsresolv/
pdirt/drv/utils/gdbm/
#define BOARD_C
/******************************************************************************
 ** Project    : pDirt
 ** Module     : board.c
 ** Author     : Various
 ** Description: The bulletin board system. Handles all bulletin boards in the
 **              MUD, and their commands.
 *****************************************************************************/

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#ifdef RS6000
#include <strings.h>
#endif
#include "kernel.h"
#include "config.h"
#include "board.h"
#include "timing.h"
#include <malloc.h>
#include "bprintf.h"
#include "log.h"
#include "parse.h"
#include "editor.h"

/* ==================== Define some things about them ====================== */
#define DELETE_LEVEL   LVL_ARCHWIZARD	/* Level when can delete any mess    */
#define OWNER_CAN_DEL		        /* Define if can delete what I wrote */
#define MAX_SUBJ_LEN   45	        /* Maximum chars allowed in subject  */
#define MAX_MESS_LEN   200	        /* Max lines per mess. (Same as mail) */
#define PBB_FILE       "bboard_ply"	/* Name of file for player bboard    */
#define WBB_FILE       "bboard_wiz"	/* Name of file for wizard bboard    */
#define AWB_FILE       "bboard_awiz"	/* Arch wizard board file name.      */
#define GDB_FILE       "bboard_god"	/* God board file name.              */
#define SBB_FILE       "bboard_singles" 

/* The instructions, printed at top of board when it is examined by a player. */

static char *Board_instr =
"You look closely at the board which has the following intructions written\n"
"on it.  Commands: &+wWRITE &*<heading> , &+wMESSAGE &*#, &+wERASE &*#\n";


/* =============== Function prototypes for use in board.c only ============= */
/* These are declared static because it is IMPORTANT that no outside files   */
/* call these. Reason: The rest of the mud does not know the types BOARD and */
/* MESS. This is to make board code easier to add to a mud.                  */

static BOARD *present_board ();
static void load_board (BOARD * b);
static void update_board (BOARD * b);
static Boolean is_owner (PMES pm, int pl);

/*
 * **  Boot the bulletin board files. NOTE, unlike most bootstrap files, the 
 * **  name of the file to boot is NOT contained in bootstrap file.
 * **            YOU MAY WANT TO EDIT SETUP VALUES HERE.
 */
void 
boot_bboards ()
{
  pbb.nomess = 0;
  strcpy (pbb.file, PBB_FILE);
  pbb.level = 0;
  
  wbb.nomess = 0;
  strcpy (wbb.file, WBB_FILE);
  wbb.level = LVL_APPREN;
  
  abb.nomess = 0;
  strcpy (abb.file, AWB_FILE);
  abb.level = LVL_ARCHWIZARD;
  
  gbb.nomess = 0;
  strcpy (gbb.file, GDB_FILE);
  gbb.level = LVL_GOD;

  sbb.nomess = 0;
  strcpy(sbb.file, SBB_FILE);  
  sbb.level = 0;

  /* boot mortal board first */
  load_board (&(pbb));
  /* Boot singles board now */
  load_board (&(sbb));
  /* boot the wizard board now */
  load_board (&(wbb));
  /* boot the arch_wizard board now */
  load_board (&(abb));
  /* boot the god board now */
  load_board (&(gbb));
  return;
}

/*
 * **  returns 0 id ob is NOT a bulletin board, and returns appropriate
 * **  number to indicate which board object represents. Also note that 
 * **  if (isa_board(ob)) will work fine.
 */
int 
isa_board (int ob)
{
  int i;
  
  /* Is it a player bboard */
  for (i = 0; bboards[i] != -1; i++)
    {
      if (bboards[i] == ob)
	return PBBOARD;
    }
  
  /* is it a wiz bboard */
  for (i = 0; wbboards[i] != -1; i++)
    {
      if (wbboards[i] == ob)
	return WBBOARD;
    }
  
  /* is it an arch_wizard bboard */
  for (i = 0; abboards[i] != -1; i++)
    {
      if (abboards[i] == ob)
	return AWZBBOARD;
    }
  /* is it a god bboard */
  for (i = 0; gbboards[i] != -1; i++)
    {
      if (gbboards[i] == ob)
	return GDBBOARD;
    }

  for (i = 0; sbboards[i] != -1; i++)
  {   if (sbboards[i] == ob)
          return SINGLESBOARD;
  }

  return 0;
}

/*
 * **  Simply returns the address of the first board it can find in room with
 * **  mynum. Mortal boards first. Null if none. This should NOT be called outside
 * **  this module, the mud does not know what a BOARD is. :)
 */
static BOARD *
present_board ()
{
  int i;
  
  for (i = 0; bboards[i] != -1; i++)
    {
      if (ploc (mynum) == oloc (bboards[i]))
	return &(pbb);
    }
  
  for (i = 0; wbboards[i] != -1; i++)
    {
      if (ploc (mynum) == oloc (wbboards[i]))
	return &(wbb);
    }
  
  for (i = 0; abboards[i] != -1; i++)
    {
      if (ploc (mynum) == oloc (abboards[i]))
	return &(abb);
    }
  
  for (i = 0; gbboards[i] != -1; i++)
    {
      if (ploc (mynum) == oloc (gbboards[i]))
	return &(gbb);
    }

  for (i = 0; sbboards[i] != -1; i++)
  {  if (ploc(mynum) == oloc(sbboards[i]))
        return &(sbb);
  }

  return ((BOARD *) NULL);
}

/*
 * **  Displays intructs on how to use the board as well as list of messages
 */

void 
lat_board (int ob)
{
  BOARD *brd = NULL;
  PMES m = NULL;
  int i = 1;
  
  switch (isa_board (ob))
    {
    case 0:			/* NOT a board */
      return;
      
    case PBBOARD:
      brd = &(pbb);
      break;
      
    case WBBOARD:
      brd = &(wbb);
      break;
      
    case AWZBBOARD:
      brd = &(abb);
      break;
      
    case GDBBOARD:
      brd = &(gbb);
      break;
    
    case SINGLESBOARD:
      brd = &(sbb);
      break;
    }
  if (brd->level > plev (mynum))
    {
      bprintf ("You look closely but can not understand the mystical writings\n");
      return;
    }
  
  bprintf ("%s", Board_instr);
  
  if (brd->nomess > 0)
     bprintf("\n&+wNr Date        Author       Subject\n&+w%s&*\n",DASHLINE);
  
  m = brd->messages;
  while (m != NULL)
    {
      bprintf ("&+w%2d &+w%-11.11s &+w%-*s &+w%-*s \n", i, m->date, PNAME_LEN, m->owner, MAX_SUBJ_LEN, m->header);
      i++;
      m = m->next;
    }

  if (brd->nomess > 0)
  {
      bprintf("&+w%s&*\n",DASHLINE);
      bprintf ("&+wThere are a total of &+w%d&+w message(s) on this board&*\n\n", brd->nomess);
  }
  else
      bprintf("\n&+wThere are no messages on this board&*\n");

  return;
}

/*
 * **  dump the boards to their files. See load_board for file format.
 */
static void 
update_board (BOARD * b)
{
  FILE *f = NULL;
  PMES m = NULL;
  
  if (b == (BOARD *) NULL)
    {
      mudlog ("BB ERROR: update null board");
      return;
    }
  if ((b->file == (char *) NULL) || (b->file[0] == '\0'))
    {
      mudlog ("BB ERROR: update to null file");
      return;
    }
  if ((f = fopen (b->file, "w")) == (FILE *) NULL)
    {
      mudlog ("BB ERROR: Could not open file %s for write", b->file);
      return;
    }
  
  fprintf (f, "%d\n", b->nomess);
  m = b->messages;
  while (m != NULL)
    {
      fprintf (f, "%s\n%s\n%s\n", m->owner, m->date, m->header);
      fprintf (f, "%d\n%s\n^\n", strlen (m->message), m->message);
      m = m->next;
    }
  fclose(f);
  return;
}

/*
 * **  Reads the file b->file and loads all the messages into memory that are in 
 * **  the file. FORMAT of the files is: (ignore the first ** each line)
 * **
 * **  <#num messages><NL>
 * **  <owner><NL>
 * **  <date><NL>
 * **  <header><NL>
 * **  <#strlen of the message><NL>
 * **  <message><NL><^><NL>
 * **  <owner><NL> ......
 */
static void 
load_board (BOARD * b)
{
  FILE *f = NULL;
  int i = 0, j = 0, size, c;
  PMES m = NULL;
  char buf[81];
  char *p;
  
  if ((f = fopen (b->file, "r")) == NULL)
    {
      /*    printf("BB ERROR: can't open %s.\n",b->file); */
      b->nomess = 0;
      return;
    }
  fscanf (f, "%d\n", &i);
  b->nomess = i;
  b->messages = NULL;
  for (; i > 0; i--)
    {
      m = (PMES) xmalloc (1, sizeof (MESS));
      m->next = (PMES) NULL;
      
      /* Load the message */
      fscanf (f, "%s\n", m->owner);
      fgets (buf, 81, f);
      if ((p = strchr (buf, '\n')) != NULL)
	{
	  *p = '\0';
	}
      m->date = COPY (buf);
      fgets (buf, 81, f);
      if ((p = strchr (buf, '\n')) != NULL)
	{
	  *p = '\0';
	}
      m->header = COPY (buf);
      fscanf (f, "%d\n", &j);
      j += 10;			/* Just in case */
      m->message = (char *) xmalloc (1, j);
      m->message[0] = '\0';
      size = 0;
      while (((c = getc (f)) != EOF) && (size < j - 1))
	{
	  if (c == '\n')
	    {
	      c = getc (f);
	      if (c == '^')
		{
		  m->message[size] = '\0';
		  size = j + 1;
		}
	      else
		{
		  m->message[size++] = '\n';
		  ungetc (c, f);
		}
	    }
	  else
	    {
	      m->message[size++] = (char) c;
	    }
	}
      
      /* Check errors and report/recover */
      if (c == EOF)		/* Eeek!! */
	{
	  printf ("BB ERROR: Eeek!! %s ended before expected\n", b->file);
	  i = -1;
	}
      else if (size < j)
	{
	  m->message[size] = '\0';
	}
      
      /* Copy message to the board list */
      if (b->messages == NULL)	/* special first case */
	{
	  b->messages = m;
	}
      else
	{
	  b->last->next = m;
	}
      b->last = m;		/* Remember last message */
    }
  fclose(f);
  return;
}

/*
 * **  The command that actually reads message from the board for the players.
 * **  VERB: message
 */
A_COMMAND(do_read)
{
  BOARD *b;
  PMES m;
  int num = 0, i;
  
  b = present_board ();
  if (b == NULL)		/* No board present               */
    {
      bprintf ("I don't see the board here.\n");
      return;
    }
  if ((brkword () == -1) && !isdigit (wordbuf[0]))
    {
      bprintf ("Which board would that message be written on then?\n");
      return;
    }
  
  if (b->level > plev (mynum))	/* Not able to read cause of level */
    {
      bprintf ("You look closely but can not understand the mystical writings\n");
      return;
    }
  
  num = atoi (wordbuf);
  if ((num > b->nomess) || (num < 1))	/* Number out of valid range      */
    {
      bprintf ("That message only exist in your imagination\n");
      return;
    }
  num--;
  for (i = 0, m = b->messages; (i < num) && (m != NULL); m = m->next, i++)
    ;
  if (m == NULL)
    {
      bprintf ("The message just vanished.\n");
      mudlog ("BB ERROR: could not find message in do_read()");
      return;
    }
  bprintf ("&+wMessage by:&+w %s\n"
           "&+wDate      :&+w %s\n"
           "&+wSubject   :&* %s\n\n",
	   m->owner, m->date, m->header);
  bprintf ("%s\n\n", m->message);
  return;
}

/*
 * **  Erase a message from the board in this room. Checks for permission.
 * **  You can erase if you can read the board and you wrote the message or
 * **  you are at or above the level defined above where you can erase any message
 * **  You must still be able to see the board of course.
 */
A_COMMAND(do_erase)
{
  BOARD *b;
  PMES m, m2;
  int num = 0, i;
  
  if ((b = present_board ()) == NULL)
    {
      bprintf ("Erase a message from what?\n");
      return;
    }
  
  if ((brkword () == -1) && !isdigit (wordbuf[0]))
    {
      bprintf ("Which board would that message be written on then?\n");
      return;
    }
  
  if (b->nomess <= 0)
    {
      bprintf ("Nothing written on it to delete.\n");
      return;
    }
  
  if (plev (mynum) < b->level)
    {
      bprintf ("You look closely but can not understand the mystical writings\n");
      return;
    }
  
  num = atoi (wordbuf);
  if ((num > b->nomess) || (num < 1))	/* Number out of valid range      */
    {
      bprintf ("That message only exist in your imagination\n");
      return;
    }
  num--;
  for (i = 0, m2 = NULL, m = b->messages; (i < num) && (m != NULL); i++)
    {
      m2 = m;
      m = m->next;
    }
  if (m == NULL)		/* Someone beat us to it */
    {
      bprintf ("The message just vanished.\n");
      mudlog ("BB ERROR: could not find message in do_erase()");
      return;
    }
  if ((plev (mynum) >= DELETE_LEVEL) || is_owner (m, mynum))
    {
      if (m2 == NULL)		/* del first message */
	{
	  b->messages = m->next;
	}
      else
	{
	  m2->next = m->next;
	}
      if (m == b->last)		/* deleting the last message */
	b->last = m2;
      
      free (m->message);
      free (m->date);
      free (m->header);
      free (m);
      b->nomess--;
      update_board (b);
    }
  else
    {
      bprintf ("I don't think so!!\n");
    }
  return;
}

/*
 * **  if owners are allowed to delete their messages then returns True if
 * **  mynum is the owner, False otherwise
 */
static Boolean 
is_owner (PMES pm, int pl)
{
#ifdef OWNER_CAN_DEL
  if (strcasecmp (pname (pl), pm->owner) == 0)
    return True;
  else
    return False;
  
#else
  return False;
#endif
}

/*
 * **  Allow players to write on the board. Uses the same writer as mail, 
 * **  except that it's handler buffers the input in memory, not writing it 
 * **  to a file.
 */
void new_board_handler (char *fn, char *param);

A_COMMAND(write_board)
{
  BOARD *b;
  char subj[MAX_COM_LEN];

  void board_handler (void *w, void *ad, int adlen);
  
  if (cur_player->aliased || cur_player->polymorphed != -1)
    {
      bprintf ("Not while aliased.\n");
      return;
    }
  if (cur_player->board != NULL)
    {
      bprintf ("You're writing on one already, don't be greedy.\n");
      return;
    }
  if ((b = present_board ()) == NULL)
    {
      bprintf ("There is no board to write on.\n");
      return;
    }
  if (plev (mynum) < b->level)
    {
      bprintf ("Strange, your pen doesn't seem to work.\n");
      return;
    }
  
  getreinput (subj);
  if (strlen (subj) > MAX_SUBJ_LEN)
    bprintf ("Warning: Subject to long, truncated.\n");
  subj[MAX_SUBJ_LEN + 1] = '\0';
  
  cur_player->board = b;	/* Remember the board + stop 2 writes at same time */

  start_editor(mynum,new_board_handler,MAX_SUBJ_LEN,subj);  
  return;
}

/*
 * **  Handle the data we get from the editor, create a message, put it in 
 * **  the board, save the board to a file, and finally free the cur_player->board
 * **  variable, allowing them to start another write.
 */
void new_board_handler (char *fn, char *param)
{
  PMES msg;
  FILE *fp;
  char b[100], subj[MAX_SUBJ_LEN + 1];
  char bigbuf[(MAX_COM_LEN + 1) * MAX_MESS_LEN];	/* More than we could want */
  BOARD *brd = (BOARD *) cur_player->board;
  int size = 0;
  
  if (brd == NULL)		/* Should never happen, but better than crashing mud   */
    {				/* if it does.                                         */
      mudlog ("Board went null on %s\n", pname (mynum));
      return;
    }
  
  strcpy (subj,param);	/* Seems to work better if copy this here */
  bigbuf[0] = '\0';
  
  if (fn == NULL)
  {  cur_player->board = NULL;
     return; 
  }
     
  fp = fopen(fn,"r");
  if (fp == NULL)
  {   bprintf("Oops lost message.. \n");
      cur_player->board = NULL;
      return;
  }
  
  while ((fgets (b, sizeof (b), fp)) != NULL)
  {
      if (!EMPTY (b))
	strcat (bigbuf, b);
  }
  fclose(fp);

  if (EMPTY (bigbuf))
  {
      bprintf ("Ok, I won't bother posting then\n");
      cur_player->board = NULL;
      return;
  }
  
  /* Check for trailing \n or \r and get rid of them */
  size = strlen (bigbuf);
  if ((bigbuf[size - 1] == '\n') || (bigbuf[size - 1] == '\r'))
    bigbuf[size - 1] = '\0';
  else if ((bigbuf[size] == '\n') || (bigbuf[size] == '\r'))
    bigbuf[size] = '\0';
  
  /* Lets create this message */
  msg = (PMES) xmalloc (1, sizeof (MESS));
  msg->message = COPY (bigbuf);
  msg->date = COPY (time2ascii (TIME_CURRENT));
  msg->header = COPY (subj);
  strcpy (msg->owner, pname (mynum));
  msg->next = NULL;
  
  if (brd->last == NULL)
    brd->messages = msg;
  else
    brd->last->next = msg;
  brd->last = msg;
  brd->nomess++;
  update_board (brd);
  
  cur_player->board = NULL;	/* Free up the board(locking write for mynum) */
  bprintf("\r%s",cur_player->prompt); 
  return;
}