ackmud/
ackmud/area/imc/
ackmud/npcs/a/
ackmud/npcs/c/
ackmud/npcs/d/
ackmud/npcs/e/
ackmud/npcs/f/
ackmud/npcs/h/
ackmud/npcs/i/
ackmud/npcs/k/
ackmud/npcs/l/
ackmud/npcs/n/
ackmud/npcs/o/
ackmud/npcs/p/
ackmud/npcs/r/
ackmud/npcs/s/
ackmud/npcs/w/
ackmud/player/c/
ackmud/player/s/
ackmud/player/z/
/****************************************************************************
 *                                                                          *
 *     _/       _/_/_/  _/    _/   _/   BOARD.C : Bulletin Board system for *
 *    _/_/     _/       _/  _/     _/   ACK! MUD.  This code may be used by *
 *   _/  _/   _/        _/_/       _/   other parties, providing some form  *
 *  _/_/_/_/   _/       _/  _/          of acknowledgement is displayed :)  *
 * _/      _/   _/_/_/  _/    _/   _/         (C)1994 Stephen Dooley        *
 *                                      Thanks to Mart for some help here.  *
 ****************************************************************************/


#if defined(macintosh)                 
include <types.h>
#else
#include <sys/types.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"

#define BOARD_DIR "boards"
#define T2000 -1                        /* Terminator for files... */



/* Local functions */
BOARD_DATA * load_board(OBJ_INDEX_DATA * pObj);
void         save_board(BOARD_DATA * board,CHAR_DATA *ch);
void finished_editing( MESSAGE_DATA * msg, char * * dest, CHAR_DATA * ch, bool saved);

/* Some locals used to manage the list of messages: */

BOARD_DATA   *  first_board = NULL;
BOARD_DATA   *	last_board = NULL;
BOARD_DATA   *	board_free = NULL;
MESSAGE_DATA *	message_free = NULL;


/**************************************************************************
 *               MAG Modified outline                                     *
 *       Use a directory called boards under area directory.              *
 *       Store a file for each board called board.vnum makes things a lot *
 *       easier, and means files can be transfered easily between boards  *
 *       use a time based means to get rid of messages.                   *
 *                                 					  *
 *       Values:							  *
 *          Value0  :  Expiry time for a message in days.                 *
 *          Value1  :  Min level of player to read the board.             *
 *          Value2  :  Max level of player to write to the board.         *
 *          Value3  :  Board number (usually it's vnum for simplicity)    *
 *									  *
 *       Uses commands in write.c for string editing.                     *
 *       This file does reading files, writing files and managing boards  *
 *									  *
 **************************************************************************/

/************************************************************************** 
 *                        Outline of BOARD.C                              *
 *                        ^^^^^^^^^^^^^^^^^^                              *
 * This code was written for use in ACK! MUD.  It should be easy to       *
 * include it into a Diku Merc 2.0+ Mud.                                  *
 *                                                                        *
 * The following functions are needed:                                    *
 * 1) Show the contents of a board to a player.                           *
 * 2) Show a message to a player                                          *
 * 3) Add to a message & finish writing a message.                        *
 * 4) Start writing a message                                             *
 * 5) Remove a message                                                    *
 * 6) Save the messages                                                   *
 * 7) Load the messages (only used at start-up)                           *
 *                                                                        *
 * Also, the code for the commands write and read are in this file.       *
 * WRITE checks for a board, and calls either 4) or 3) above.             *
 * READ calls 2) above.  The LOOK function was ammended to allow players  *
 * to type 'look board' or 'look at board' which calls 1) above.          *
 *                                                                        *
 * MESSAGE DATA holds the vnum of the board where the message was written *
 * and the message info.  There is no seperate save file or structure for *
 * each board.  Instead, they are all stored together.  I have used       *
 * OBJ_TYPE 23 for boards, with the following values used:                *
 * value 0: max number of messages allowed                                *
 * value 1: min level of player to read the board                         *
 * value 2: min level of player to write on the board                     *
 * value 3: the vnum of the board...NOT the vnum of the room...           *
 **************************************************************************/                                                                       
 
/**************************************************************************
 * Ick ick ick!  Remove all the dammed builder functions, and use the     *
 * general merc memory functions.  -- Altrag                              *
 **************************************************************************/


void do_show_contents( CHAR_DATA * ch, OBJ_DATA * obj )
{
   /* Show the list of messages that are present on the board that ch is
    * looking at, indicated by board_vnum...
    */

    MESSAGE_DATA      *msg;   
    BOARD_DATA        *board;
    OBJ_INDEX_DATA    *pObj;
    char              buf[MAX_INPUT_LENGTH];
    int               cnt = 0;
    int               board_num;
    
    pObj=obj->pIndexData;
    board_num=pObj->value[3];
       
    /* First find the board, and if not there, create one. */
    for (board=first_board; board != NULL; board=board->next)
    {
     if (board->vnum==board_num)
      break;
    }
    
    if (board==NULL)
     board=load_board(pObj);
     
    /* check here to see if player allowed to read board */

    if (board->min_read_lev > get_trust(ch) )
    {
      send_to_char("You are not allowed to look at this board.\n\r",ch);
      return;
    }
    
    if ( (board->clan != 0) 
         && !IS_NPC(ch) 
         && ch->pcdata->clan != (board->clan -1)
       )
    {
      send_to_char("You are not of the right clan to read this board.\n\r",ch);
      return;
    }  


    send_to_char( "This is a notice board.\n\r", ch );
    send_to_char( "Type READ <num> to read message <num>, and WRITE <title> to post.\n\r", ch );
    send_to_char( "Type WRITE to: <player> for a private message.\n\r",ch);
    send_to_char( 
    "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\r", ch );

    
    for ( msg = board->first_message; msg != NULL; msg = msg->next )
    {
            cnt++;
            sprintf( buf, "[%3d] %12s : %s",
                cnt, msg->author, msg->title );
            send_to_char( buf, ch );
        
    }

    if ( cnt == 0 ) /* then there were no messages here */
    {
        send_to_char( "         There are no messages right now!\n\r", ch );
    }
    
    send_to_char( 
    "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\r", ch );

    return;
}

BOARD_DATA * load_board(OBJ_INDEX_DATA * pObj)
{
     FILE * board_file;
     char   buf[255];
     char * word;
     char   letter;
     time_t message_time;
     time_t expiry_time;
     BOARD_DATA * board;
     MESSAGE_DATA * message;

     GET_FREE(board, board_free);
     LINK(board, first_board, last_board, next, prev);
     
     board->expiry_time=pObj->value[0];
     board->min_read_lev=pObj->value[1];
     board->min_write_lev=pObj->value[2];
     board->vnum=pObj->value[3];
     board->clan=0;
     board->first_message=NULL;
     board->last_message=NULL;
     
     fclose(fpReserve);
     
     sprintf(buf,"%s/board.%i",BOARD_DIR,board->vnum);
     
     if ( (board_file=fopen(buf,"r")) != NULL )
     {
      /* Read in Optional board parameters */
      for ( ; ; )
      {
       if (feof(board_file))
        break;
        
       word=fread_word(board_file);
       if (!str_cmp(word,"ExpiryTime"))
       {
        board->expiry_time=fread_number(board_file);
        fread_to_eol(board_file);
       }
       if (!str_cmp(word,"MinReadLev"))
       {
        board->min_read_lev=fread_number(board_file);
        fread_to_eol(board_file);
       }
       if (!str_cmp(word,"MinWriteLev"))
       {
        board->min_write_lev=fread_number(board_file);
        fread_to_eol(board_file);
       }
       if (!str_cmp(word,"Clan"))
       {
        board->clan=fread_number(board_file);
        fread_to_eol(board_file);
       }
       if (!str_cmp(word,"Messages"))
       {
        fread_to_eol(board_file);
        break;
       }
      }
      
      if (board->expiry_time > 0)
        expiry_time=time(NULL)-(board->expiry_time)*3600*24;
      else
        expiry_time=0;
      
      /* Now read in messages */
      for ( ; ; )
      {
       if (feof(board_file))
        break;
      
       letter=fread_letter(board_file);
       if (letter=='S')
        break;
       
       if (letter!='M')
       {
        bug("Letter in message file not M",0);
        break;
       }
       
       /* check time */
       message_time=(time_t) fread_number(board_file);
       if (feof(board_file))
        break;
      
       if (message_time < expiry_time )
       {
        char	*dumpme;

        dumpme = fread_string(board_file); /* author  */
        free_string( dumpme );
        dumpme = fread_string(board_file); /* title   */
        free_string( dumpme );
        dumpme = fread_string(board_file); /* message */
        free_string( dumpme );
       }
       else
       {
        GET_FREE(message, message_free);
        message->datetime=message_time;        
        message->author=fread_string(board_file);
        message->title=fread_string(board_file);
        message->message=fread_string(board_file);
        LINK(message, board->first_message, board->last_message, next, prev);
        message->board=board;
       }
      }
      
      /* Now close file */
      fclose(board_file);
      fpReserve=fopen(NULL_FILE,"r");
     } 
     
     return board;
}

void save_board(BOARD_DATA * board,CHAR_DATA * ch)
{
     char       buf[MAX_STRING_LENGTH];
     FILE   *   board_file;
     MESSAGE_DATA * message;
     
     fclose(fpReserve);
     
     
     sprintf(buf,"%s/board.%i",BOARD_DIR,board->vnum);
     if ( (board_file=fopen(buf,"w")) == NULL )
     {
        send_to_char("Cannot save board, please contact an immortal.\n\r",ch);
        bug("Could not save file board.%i.",board->vnum);
        fpReserve=fopen(NULL_FILE,"r");
        return;
     }
     
     fprintf(board_file,"ExpiryTime  %i\n",board->expiry_time);
     fprintf(board_file,"MinReadLev  %i\n",board->min_read_lev);
     fprintf(board_file,"MaxWriteLev %i\n",board->min_write_lev);
     fprintf(board_file,"Clan        %i\n",board->clan);
     
     /* Now print messages */
     fprintf(board_file,"Messages\n");
     
     for ( message = board->first_message; message; message = message->next )
     {
      fprintf(board_file,"M%i\n",(int) (message->datetime));
     
      strcpy(buf,message->author); /* Must do copy, not allowed to change string directly */
      smash_tilde(buf);
      fprintf(board_file,"%s~\n",buf);
      
      strcpy(buf,message->title);
      smash_tilde(buf);
      fprintf(board_file,"%s~\n",buf);
      
      strcpy(buf,message->message);
      smash_tilde(buf);
      fprintf(board_file,"%s~\n",buf);
      
     }
     
     fprintf(board_file,"S\n");
     
     fclose(board_file);
     fpReserve=fopen(NULL_FILE,"r");
     
     return;
}

void do_delete( CHAR_DATA *ch, char * argument)
{
    OBJ_DATA       *   object;
    BOARD_DATA     *   board;
    MESSAGE_DATA   *   msg;
    OBJ_INDEX_DATA *   pObj;
    int                vnum;
    int                mess_num; 
    int                cnt=0;

    if ( IS_NPC( ch ) )
    {
       send_to_char( "NPCs may *not* delete messages.  So there.\n\r", ch );
       return;
    }
    
    if ( argument[0] == '\0' )
    {
        send_to_char( "You need to specify a message number to delete!\n\r", ch );
        return;
    }

    for ( object = ch->in_room->first_content ; object != NULL; object = object -> next_in_room )
    {
        if (object->item_type == ITEM_BOARD)
            break;
    }
    
    if (object == NULL)
    {
      send_to_char("Delete on what?\n\r.",ch);
      return;
    }
    
    pObj=object->pIndexData;
    vnum=pObj->value[3];
    
  /* First find the board, and if not there, create one. */
    for (board=first_board; board != NULL; board=board->next)
    {
     if (board->vnum==vnum)
      break;
    }
    
    if (board==NULL)
     board=load_board(pObj);
     
    /* check here to see if player allowed to write to board */

    if (board->min_write_lev > get_trust(ch) )
    {
      send_to_char("You are not allowed to delete on this board.\n\r",ch);
      return;
    }
    
    if ( (board->clan != 0) 
         && ch->pcdata->clan != (board->clan -1)
       )
    {
      send_to_char("You are not of the right clan to delete on this board.\n\r",ch);
      return;
    }
    

    cnt=0;
    mess_num= is_number(argument) ? atoi(argument) : 0;
    
    for ( msg = board->first_message; msg != NULL; msg = msg->next )
      if ( ++cnt == mess_num ) /* then this the message!!! */
        break;

    if (msg==NULL)
    {
        send_to_char( "No such message!\n\r", ch );
        return;
    }
    
    /* See if person is writer or is recipient */
    if ( str_cmp(ch->name,msg->author) && !is_name(ch->name,msg->title)
         && get_trust(ch) < MAX_LEVEL 
         && str_cmp(ch->name,clan_table[board->clan].leader) )
    {
        send_to_char( "Not your message.\n\r",ch);
        return;
    }
    
    /* Now delete message */
    
    UNLINK(msg, board->first_message, board->last_message, next, prev);
    free_string(msg->author);
    free_string(msg->title);
    free_string(msg->message);
    PUT_FREE(msg, message_free);
    
    save_board(board,ch);     
   
    return;  
}

void do_show_message( CHAR_DATA *ch ,int mess_num, OBJ_DATA * obj )
{
   /* Show message <mess_num> to character. 
    * check that message vnum == board vnum
    */

   BOARD_DATA   * board;
   OBJ_INDEX_DATA * pObj;
   int            vnum;
   MESSAGE_DATA * msg;
   int            cnt = 0;
   bool           mfound = FALSE;
   char           buf[MAX_STRING_LENGTH];
   char           to_check[MAX_INPUT_LENGTH];
   char         * to_person;
   char           private_name[MAX_INPUT_LENGTH];   
   
   pObj=obj->pIndexData;
   vnum=pObj->value[3];
   
  /* First find the board, and if not there, create one. */
    for (board=first_board; board != NULL; board=board->next)
    {
     if (board->vnum==vnum)
      break;
    }
    
    if (board==NULL)
     board=load_board(pObj);
     
    /* check here to see if player allowed to read board */

    if (board->min_read_lev > get_trust(ch) )
    {
      send_to_char("You are not allowed to look at this board.\n\r",ch);
      return;
    }
    
    if ( (board->clan != 0) 
         && !IS_NPC(ch) 
         && ch->pcdata->clan != (board->clan -1)
       )
    {
      send_to_char("You are not of the right clan to read this board.\n\r",ch);
      return;
    }  

   for ( msg = board->first_message; msg != NULL; msg = msg->next )
   {
     if ( ++cnt == mess_num ) /* then this the message!!! */
     {
       mfound = TRUE;
              
       to_person = one_argument(msg->title,to_check);
       to_person = one_argument( to_person, private_name );
       if (  !str_cmp(to_check,"to:")
          && str_prefix(private_name,ch->name)
          && str_cmp(msg->author,ch->name) )                  
        {
         send_to_char("This is a private message.\n\r",ch);
         break;
       }
       sprintf( buf, "** [%d] %12s : %s ** \n\r\n\r%s\n\r",
                cnt, msg->author, msg->title , msg->message );
       send_to_char( buf, ch );
       break;
    }
  }

  if (!mfound)
  {
    send_to_char( "No such message!\n\r", ch );
  }
   
  return;  
}

void do_write( CHAR_DATA *ch, char *argument )
{
    OBJ_DATA       *   object;
    BOARD_DATA     *   board;
    MESSAGE_DATA   *   msg;
    OBJ_INDEX_DATA *   pObj;
    int                vnum;
    extern char        str_empty[1];
    char buf[MAX_STRING_LENGTH];

    if ( IS_NPC( ch ) )
    {
       send_to_char( "NPCs may *not* write messages.  So there.\n\r", ch );
       return;
    }
    
    if ( argument[0] == '\0' )
    {
        send_to_char( "You need to specify a header for your message!\n\r", ch );
        return;
    }

    for ( object = ch->in_room->first_content ; object != NULL; object = object -> next_in_room )
    {
        if (object->item_type == ITEM_BOARD)
            break;
    }
    
    if (object == NULL)
    {
      send_to_char("Write on what?\n\r.",ch);
      return;
    }
    
    pObj=object->pIndexData;
    vnum=pObj->value[3];
    
  /* First find the board, and if not there, create one. */
    for (board=first_board; board != NULL; board=board->next)
    {
     if (board->vnum==vnum)
      break;
    }
    
    if (board==NULL)
     board=load_board(pObj);
     
    /* check here to see if player allowed to write to board */

    if (board->min_write_lev > get_trust(ch) )
    {
      send_to_char("You are not allowed to write on this board.\n\r",ch);
      return;
    }
    
    if ( (board->clan != 0) 
         && ch->pcdata->clan != (board->clan -1)
       )
    {
      send_to_char("You are not of the right clan to write on this board.\n\r",ch);
      return;
    }
    
    GET_FREE(msg, message_free);               /* Dont put message in list till we  */
    msg->datetime=time(NULL);		       /* we are sure we can edit.          */
    sprintf( buf, "%s @@a%s@@N", argument, (char *) ctime( &current_time )  );
    if ( msg->title != NULL )
      free_string( msg->title );
    msg->title=str_dup(buf);
    if ( msg->author != NULL )
    free_string( msg->author );
    msg->author=str_dup(ch->name);
    msg->message=NULL;
    msg->board=board;
    
    /* Now actually run the edit prog. */
    write_start(&msg->message,finished_editing,msg,ch);
 
    if (msg->message != &str_empty[0] )
    {
     send_to_char("Editing message. Type .help for help.\n\r",ch);
     LINK(msg, board->first_message, board->last_message, next, prev);
    }
    else
    {
     send_to_char("Could not add message.\n\r",ch);
     PUT_FREE(msg, message_free);
    }
    return;
}
    

/* Deals with taking message out of list if user aborts... */

void finished_editing( MESSAGE_DATA * msg, char * * dest, CHAR_DATA * ch, bool saved)
{
#ifdef CHECK_VALID_BOARD
  MESSAGE_DATA * SrchMsg;
#endif

  if (!saved)
  {
#ifdef CHECK_VALID_BOARD
    for (SrchMsg=msg->board->messages;SrchMsg != NULL;SrchMsg=SrchMsg->next)
      if (SrchMsg==msg)
        break;
  
    if (SrchMsg==NULL)
    {
      /* Could not find this message in board list, just lose memory. */
      return;
    }
#endif
  
    UNLINK(msg, msg->board->first_message, msg->board->last_message, next, prev);
    PUT_FREE(msg, message_free);
  }
  else
  {
    save_board(msg->board,ch);
  }
  return;
}
    
void do_read( CHAR_DATA *ch, char *argument )
{
    OBJ_DATA *  obj;


    if ( ( argument[0] == '\0' ) || !is_number( argument ) )
    {
        send_to_char( "Read what??\n\r", ch );
        return;
    }
    
    for ( obj = ch->in_room->first_content; obj != NULL; obj = obj->next_in_room )    
    {
        if ( obj->item_type == ITEM_BOARD )
            break;
    }

    if ( obj == NULL )
    {
        send_to_char( "Read What??\n\r", ch );
        return;
    }

    /* Hopefully, by now there should be a board in the room, and the
     * player should have supplied some sort of argument....
     */

    do_show_message( ch, atoi( argument ), obj );
    return;
}





void do_edit_message( CHAR_DATA *ch ,int mess_num, OBJ_DATA * obj )
{
   /* Show message <mess_num> to character. 
    * check that message vnum == board vnum
    */

   BOARD_DATA   * board;
   OBJ_INDEX_DATA * pObj;
   int            vnum;
   MESSAGE_DATA * msg;
   int            cnt = 0;
   bool           mfound = FALSE;
   
   pObj=obj->pIndexData;
   vnum=pObj->value[3];
   
  /* First find the board, and if not there, create one. */
    for (board=first_board; board != NULL; board=board->next)
    {
     if (board->vnum==vnum)
      break;
    }
    
    if (board==NULL)
     board=load_board(pObj);
     
    /* check here to see if player allowed to read board */

    if (board->min_read_lev > get_trust(ch) )
    {
      send_to_char("You are not allowed to even look at this board!\n\r",ch);
      return;
    }
    
    if ( (board->clan != 0) 
         && !IS_NPC(ch) 
         && ch->pcdata->clan != (board->clan -1)
       )
    {
      send_to_char("You are not of the right clan to even read this board!\n\r",ch);
      return;
    }  

   for ( msg = board->first_message; msg != NULL; msg = msg->next )
   {
     if ( ++cnt == mess_num ) /* then this the message!!! */
     {
       mfound = TRUE;
               
       if ( str_cmp(msg->author,ch->name) )                  
       {
         send_to_char("Not your message to edit!\n\r",ch);
         return;
       }
       else
       {
         build_strdup( &msg->message, "$edit", TRUE, ch);
       }   
           
     }
   }
   if ( !mfound )
      send_to_char( "No such message!\n\r", ch );
   
   return;
}

void do_edit( CHAR_DATA *ch, char *argument )
{
    OBJ_DATA *  obj;


    if ( ( argument[0] == '\0' ) || !is_number( argument ) )
    {
        send_to_char( "Read what??\n\r", ch );
        return;
    }
    
    for ( obj = ch->in_room->first_content; obj != NULL; obj = obj->next_in_room )    
    {
        if ( obj->item_type == ITEM_BOARD )
            break;
    }

    if ( obj == NULL )
    {
        send_to_char( "Read What??\n\r", ch );
        return;
    }

    /* Hopefully, by now there should be a board in the room, and the
     * player should have supplied some sort of argument....
     */

    do_edit_message( ch, atoi( argument ), obj );
    return;
}