/*******************************\
* MCOM.C			*
* (C)1986-87, Patrick E. Hughes	*
\*******************************/

#include <exec/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <intuition/intuition.h>
#include <time.h>
#include "System.h"
#include "Defines.h"

/***********************\
* GLOBAL VARIABLE LIST	*
\***********************/

extern struct System System;

extern int Boards_Are_Active, Libraries_Are_Active;
extern int Bulletins_Are_Active, Mail_Is_Active;
extern int Whence_The_Logon;
extern struct User User;
extern long lseek();

static char Message_Buffer[2510] = {'\0'}; /* including safety space */

/********************/
int MCom()
{
int ch, stat, command_accepted, ListNumber;
struct Board_Header *bh;
char string[133];

if(Boards_Are_Active==0) { return(FAILURE); }

StatRollMessage("Messages");
PutStr("Message Bases\r\n");

FOREVER
	{
	Check_Online_Status();
	PutStr("\r\nN>ew  S>ome  A>ll  L>ist  Q>uit\r\n");
	sprintf(string,"%s Message Base Choices? ",System.Name);
	PutStr(string);
	FOREVER
		{
		ch=ReadChar(120L);
		if(ch==TIMEOUT) { return(ch); }
		Check_Online_Status();
		switch(ch)
			{
			case 'a': /* ALL */
			case 'A':
				PutStr("ALL Message Bases\r\n\r\n");
				bh=System.Boards_List;
				ListNumber=0;
				do{
					if((User.Sec_Board>=bh->Read_Low &&
					   User.Sec_Board<=bh->Read_High) ||
					   (User.Sec_Board>=bh->Write_Low &&
					   User.Sec_Board<=bh->Write_High)){
					   sprintf(string,"[%d] %s\r\n",
					   	++ListNumber,bh->Name);
						PutStr(string);
						stat=Msg_Prompt(bh,ListNumber);
						if(stat==TIMEOUT ||
						   stat==NO_CARRIER)
							return(stat);
						if(stat=='@') return(SUCCESS);
					}
					Check_Online_Status();
					bh=bh->Next_Board;
				}while(bh!=NULL);
				PutStr("Completed visiting ALL\r\n");
				command_accepted=SUCCESS;
				break;
			case 'n': /* NEW ONLY */
			case 'N':
				PutStr("NEW Message Bases\r\n\r\n");
				bh=System.Boards_List;
				ListNumber=0;
				do{
					if(User.Sec_Board>=bh->Read_Low &&
					   User.Sec_Board<=bh->Read_High &&
					   User.Time_Last_On<bh->Latest_Time){
					   sprintf(string,"\r\n[%d] %s\r\n",
					   	++ListNumber,bh->Name);
						PutStr(string);
						stat=Msg_ReadNew(bh);
						if(stat==TIMEOUT ||
						   stat==NO_CARRIER)
							return(stat);
						stat=Msg_Prompt(bh,ListNumber);
						if(stat==TIMEOUT ||
						   stat==NO_CARRIER)
							return(stat);
						if(stat=='@') return(SUCCESS);
					}
					Check_Online_Status();
					bh=bh->Next_Board;
				}while(bh!=NULL);
				PutStr("\r\nCompleted visiting NEW\r\n");
				command_accepted=SUCCESS;
				break;
			case 's': /* selective */
			case 'S':
				PutStr("SOME Message Bases\r\n");
				bh=System.Boards_List;
				ListNumber=0;
				do{
					if((User.Sec_Board>=bh->Read_Low &&
					   User.Sec_Board<=bh->Read_High) ||
					   (User.Sec_Board>=bh->Write_Low &&
					   User.Sec_Board<=bh->Write_High)){
					   	sprintf(string,
							"\r\nVisit [%d] %s ?",
							++ListNumber,bh->Name);
						PutStr(string);
						ch=ReadChar(120L);
						if(ch==TIMEOUT) { return(ch); }
						if(toupper(ch)=='Y'){
							PutStr("Yes\r\n");
						stat=Msg_Prompt(bh,ListNumber);
							if(stat==TIMEOUT ||
							   stat==NO_CARRIER)
								return(stat);
							if(stat=='@')
							  return(SUCCESS);
						}
						if(toupper(ch)=='Q'
						|| ch=='@'){
						PutStr("Quit\r\n");
						break;
						}
						else PutStr("No\r\n");
					}
					Check_Online_Status();
					bh=bh->Next_Board;
				}while(bh!=NULL);
				PutStr("\r\nCompleted visiting SOME\r\n");
				command_accepted=SUCCESS;
				break;
			case 'L': /* LIST */
			case 'l':
				PutStr("LIST Message Bases\r\n\r\n");
				bh=System.Boards_List;
				ListNumber=0;
				do{
					if((User.Sec_Board>=bh->Read_Low &&
					   User.Sec_Board<=bh->Read_High) ||
					   (User.Sec_Board>=bh->Write_Low &&
					   User.Sec_Board<=bh->Write_High)){
						sprintf(string,
						"[%d] %s\r\n",
						++ListNumber,bh->Name);
						PutStr(string);
					}
					Check_Online_Status();
					bh=bh->Next_Board;
				}while(bh!=NULL);
				PutStr("\r\nCompleted LIST\r\n");
				command_accepted=SUCCESS;
				break;
			case 'Q': /* outta here */
			case 'q':
			case '@':
				PutStr("QUIT to Main\r\n");
				return(SUCCESS);
				break;
			case '?':
			case '/':
				PutStr("Command Summary\r\n");
				strcpy(string,System.Location);
				strcat(string,"Messages.Help");
				stat=MenuSend(string);
				if(stat==TIMEOUT || stat==NO_CARRIER)
					return(stat);
				command_accepted=SUCCESS;
				break;
			case '\000': /* FN-KEY HIT */
				command_accepted=SUCCESS;
				PutStr("\r\n");
				break;
			default:
				PurgeLine();
				command_accepted=FAILURE;
			}
		if(command_accepted==FAILURE) { continue; }
		break;
		}
	}
}

/*********************/
static int Msg_Prompt(bh,BoardNumber)
	struct Board_Header *bh;
	int BoardNumber;
{
int ch, stat, command_accepted;
char string[81];

FOREVER
	{
	Check_Online_Status();
	PutStr("\r\nN>ew R>ead I>mmediate W>rite Q>uit @>MainMenu\r\n");
	if(User.Sec_Status>=100)
		{
		PutStr("D>elete\r\n");
		}
	sprintf(string,"[%d] %s Options? ",BoardNumber,bh->Name);
	PutStr(string);
	FOREVER
		{
		ch=ReadChar(120L);
		if(ch==TIMEOUT) { return(ch); }
		Check_Online_Status();
		switch(ch)
			{
			case 'n': /* NEW READING */
			case 'N':
				if(User.Sec_Board<bh->Read_Low ||
				   User.Sec_Board>bh->Read_High){
					command_accepted=FAILURE;
					break;
				}
				stat=Msg_ReadNew(bh);
				if(stat=='@')
					{
					PutStr("@ MainMenu\r\n");
					}
				command_accepted=SUCCESS;
				break;
			case 'd': /* deletions */
			case 'D':
				if(User.Sec_Status<100){
					command_accepted=FAILURE;
					break;
				}
				stat=Msg_Delete_Frontend(bh);
				if(stat=='@')
					{
					PutStr("@ MainMenu\r\n");
					}
				command_accepted=SUCCESS;
				break;
			case 'r': /* read */
			case 'R':
				if(User.Sec_Board<bh->Read_Low ||
				   User.Sec_Board>bh->Read_High){
					command_accepted=FAILURE;
					break;
				}
				stat=Msg_Read_Frontend(bh);
				if(stat=='@')
					{
					PutStr("@ MainMenu\r\n");
					}
				command_accepted=SUCCESS;
				break;
			case 'w': /* write */
			case 'W':
				if(User.Sec_Board<bh->Write_Low ||
				   User.Sec_Board>bh->Write_High){
					command_accepted=FAILURE;
					break;
				}
				stat=Msg_Write(bh);
				if(stat==SUCCESS)
					{
					Append_Stat(STAT_MESSAGE);
					++User.Messages_Posted;
					}
				if(stat=='@')
					{
					PutStr("@ MainMenu\r\n");
					}
				command_accepted=SUCCESS;
				break;
			case 'i': /* immediate */
			case 'I':
				if(User.Sec_Board<bh->Read_Low ||
				   User.Sec_Board>bh->Read_High){
					command_accepted=FAILURE;
					break;
				}
				stat=Msg_Immediate(bh);
				if(stat=='@')
					{
					PutStr("@ MainMenu\r\n");
					}
				command_accepted=SUCCESS;
				break;
			case 'Q': /* exit */
			case 'q':
				PutStr("Quit to next\r\n");
				return(SUCCESS);
				break;
			case '@': /* main */
				PutStr("@ MainMenu\r\n");
				return('@');
				break;
			case '?':
			case '/':
				PutStr("Command Summary\r\n");
				strcpy(string,System.Location);
				strcat(string,"MsgPrompt.Help");
				stat=MenuSend(string);
				command_accepted=SUCCESS;
				break;
			case '\000': /* FN-KEY HIT */
				command_accepted=SUCCESS;
				PutStr("\r\n");
				break;
			default:
				PurgeLine();
				command_accepted=FAILURE;
				break;
			}
		if(command_accepted==TIMEOUT) { return(TIMEOUT); }
		if(command_accepted==NO_CARRIER) { return(NO_CARRIER); }
		if(command_accepted==FAILURE) { continue; }
		break;
		}
	}
}

/********************/
static int Msg_ReadNew(bh)
	struct Board_Header *bh;
{
struct Board_Data *dh;
int number;

PutStr("New Messages\r\n");

if(bh->Highest_Post==0)
	{
	PutStr("No messages in this base\r\n");
	return(SUCCESS);
	}

if(bh->Latest_Time<=User.Time_Last_On)
	{
	PutStr("Nothing new today\r\n");
	return(SUCCESS);
	}

for(number=0;number<bh->Highest_Post;++number)
	{
	dh=bh->Start_of_Data;
	dh+=number;
	if(dh->Time>User.Time_Last_On) { break; }
	}

return(Msg_Read(bh,number+1,bh->Highest_Post));
}

/********************/
static int Msg_Read(bh,from,to)
	struct Board_Header *bh;
	int from,to;
{
struct Board_Data *bd;
int ch, number, stat, fd, count;
long seekstat, lslot, lsize;
char filename[133], string[81], *timestr;

--from;
--to;

/* OPEN THE DATA FILE */
strcpy(filename,bh->Location);
strcat(filename,"Board.Data");
fd=open(filename,O_RDONLY);
if(fd==(-1))
	{
	PutStr("File error\r\n");
	PutStr(filename);
	PutStr(" cannot be accessed\r\n\r\n");
	return(SUCCESS);
	}

for(number=from;number<=to && number<bh->Highest_Post;++number)
	{
	bd=bh->Start_of_Data;
	bd+=number;

	/* SEEK TO FILE POSITION */
	seekstat=lseek(fd,(long)(bd->Slot-1)*2500L,0);
	if(seekstat!=(long)(bd->Slot-1)*2500L)
		{
		sprintf(string,"Message %hu seek error\r\n",number+1);
		PutStr(string);
		continue;
		}

	/* READ MESSAGE INTO BUFFER */
	stat=read(fd,Message_Buffer,2500);
	if(stat!=2500)
		{
		sprintf(string,"Message %hu read error\r\n",number+1);
		PutStr(string);
		continue;
		}

	/* SEND THE MESSAGE ITSELF */
	stat=Send_Message(bh,bd,Message_Buffer,number+1);
	if(stat==TIMEOUT || stat==NO_CARRIER) { close(fd); return(stat); }
	if(stat=='@') { return(stat); }

	/* BETWEEN MESSAGE PROMPT */
	stat=Between_Msg_Prompt(bh,bd,Message_Buffer,number+1);
	if(stat==TIMEOUT || stat==NO_CARRIER) { return(stat); }
	if(stat==0) { break; } /* QUIT */
	if(stat==1) { continue; } /* CONTINUE */
	if(stat==2) { --number; continue; } /* AGAIN */
	if(stat==3 && number>0) { number-=2; continue; } /* BACKWARDS */
	if(stat==3 && number==0){ --number; continue; } /* defaults AGAIN */
	if(stat=='@') { return(stat); }
	}

close(fd);
}

/***********************/
static int Send_Message(bh,bd,msg,number)
	struct Board_Header *bh;
	struct Board_Data *bd;
	char msg[];
	int number;
{
int count, ch, more;
char stat, *ptr, *timestr, output[133], string[81];

	/* SEND OUT HEADER INFO */
sprintf(string,"\r\nNumber: [%hu] of [%hu]\r\n",number,bh->Highest_Post);
	PutStr(string);
	if(strlen(bd->Title)>0)
		{
		sprintf(string," Title: %s\r\n",bd->Title);
		PutStr(string);
		}
	sprintf(string,"Author: %s [%hu]\r\n",bd->Author,bd->Person_Code);
	PutStr(string);
	timestr=ctime(&bd->Time);
	timestr[strlen(timestr)-1]='\0';
	sprintf(string,"  Time: %s\r\n\r\n",timestr);
	PutStr(string);

for(count=0,more=5;;)
	{
	ptr=&msg[count];
	for(;msg[count]!='\n' && msg[count]!='\0';++count) { ; }
	if(msg[count]=='\0') { break; }
	msg[count]='\0';
	strcpy(output,ptr);
	strcat(output,"\r\n");
	PutStr(output);
	msg[count]='\n';
	++count;++more;
	if(more>=22)
		{
		more=0;
		PutStr("More..");
		ch=ReadChar(120L);
		if(ch==TIMEOUT || ch==NO_CARRIER) { return(ch); }
		if(ch=='Q' || ch=='q' || ch=='N' || ch=='n')
			{
			PutStr("\b\b\b\b\b\b      \b\b\b\b\b\b\r\n");
			return(SUCCESS);
			}
		PutStr("\b\b\b\b\b\b      \b\b\b\b\b\b");
		}
	/* CHECK FOR INPUT && CARRIER */
	Check_Online_Status();
	if(CheckInput())
		{
		stat=ReadChar(120L);
		switch(stat)
			{
			case '\023': /* ^S Pause */
			case 'S':
			case 's':
			case 'P':
			case 'p':
			case ' ':
				PurgeLine();
				PutStr("Any key..");
				stat=ReadChar(120L);
			    if(stat==TIMEOUT||stat==NO_CARRIER){return(stat);}
			PutStr("\b\b\b\b\b\b\b\b\b         \b\b\b\b\b\b\b\b\b");
				break;
			case '\033': /* ESC Abort */
			case '\003': /* ^C */
			case '\020': /* ^P */
			case '\031': /* ^Y */
			case 'Q':
			case 'q':
			case 'X':
			case 'x':
				PutStr("\r\nAbort\r\n");
				goto SendMark1;
				break;
			default:
				PurgeLine();
				break;
			}
		}
	}
strcpy(output,ptr);
strcat(output,"\r\n");
PutStr(output);

SendMark1:

return(SUCCESS);
}

/***********************/
static int Store_Message(bh,dh,msg)
	struct Board_Header *bh;
	struct Board_Data *dh;	/* containing title-author-etc info */
	char msg[];
{
struct Board_Data *copy;
char string[133];
int fd, stat, count;
long seekstat, lsize, lslot;

lsize=(long)2500;

stat=Find_Open_Slot(bh,dh);
if(stat==FAILURE)
	{
	PutStr("Space can not be made\r\n");
	PutStr("Your message was not saved\r\n");
	return(FAILURE);
	}

strcpy(string,bh->Location);
strcat(string,"Board.Data");
fd=open(string,O_WRONLY);
if(fd==(-1))
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" open error.\r\nYour message was not saved\r\n");
	return(FAILURE);
	}

/* SEEK TO FILE POSITION */
seekstat=lseek(fd,(long)(dh->Slot-1)*2500L,0);
if(seekstat!=(long)(dh->Slot-1)*2500L)
	{
	PutStr("Message seek error\r\n");
	close(fd);
	return(FAILURE);
	}

stat=write(fd,msg,2500);
if(stat!=2500)
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" write error.\r\nYour message was not saved\r\n");
	close(fd);
	return(FAILURE);
	}

stat=close(fd);
if(stat==(-1))
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" close error.\r\nYour message was not saved\r\n");
	close(fd);
	return(FAILURE);
	}

copy=bh->Start_of_Data;
copy+=bh->Highest_Post; /* Highest_Post is at most Maximum-1 */
++bh->Highest_Post;
dh->Time=time(NULL);
bh->Latest_Time=dh->Time;
*copy=*dh;

return(Flush_Keys(bh));
}

/*********************/
static int Find_Open_Slot(bh,dh)
	struct Board_Header *bh;
	struct Board_Data *dh;
{
int slot, the_search, y;
struct Board_Data *data;
char str[81];

if(bh->Highest_Post==bh->Maximum)
	{
	if((y=Make_Open_Slot(bh))==FAILURE) { return(FAILURE); }
	}

for(slot=1;slot<=bh->Maximum;++slot)
	{
	the_search=SUCCESS;
	for(y=0;y<bh->Maximum;++y)
		{
		data=bh->Start_of_Data;
		data+=y;
		if(slot==data->Slot){ the_search=FAILURE; break; }
		}
	if(the_search==SUCCESS)
		{
		dh->Slot=slot;
		return(the_search);
		}
	}

return(FAILURE);
}

/**********************/
static int Flush_Keys(bh)
	struct Board_Header *bh;
{
char string[133];
int fd, stat;

strcpy(string,bh->Location);
strcat(string,"Board.Keys");

fd=open(string,O_WRONLY);
if(fd==(-1))
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" can not be opened, possible corrupt data\r\n");
	return(FAILURE);
	}

stat=write(fd,bh->Start_of_Data,bh->Maximum*sizeof(struct Board_Data));
if(stat!=bh->Maximum*sizeof(struct Board_Data))
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" can not be written, possible corrupt data\r\n");
	close(fd);
	return(FAILURE);
	}

stat=close(fd);
if(stat==(-1))
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" can not be closed, possible corrupt data\r\n");
	return(FAILURE);
	}

return(SUCCESS);
}

/****************************/
static int Msg_Read_Frontend(bh)
	struct Board_Header *bh;
{
int from, to, stat;
char string[81], holder[6];

if(bh->Highest_Post==0)
	{
	PutStr("Read Messages\r\n");
	PutStr("There are no messages in this base\r\n");
	return(SUCCESS);
	}

PutStr("Read Messages\r\n");

Read_Front1:
Check_Online_Status();
sprintf(string,"\r\nRead FROM [1,%d]? ",bh->Highest_Post);
PutStr(string);
stat=LineInput("1",string,5,120L);
if(stat==TIMEOUT) { return(stat); }
from=atoi(string);
if(from==0) { return(SUCCESS); }
if(from>bh->Highest_Post){
    sprintf(string,"Too high. %d is the highest message\r\n",bh->Highest_Post);
	PutStr(string);
	goto Read_Front1;
}
Read_Front2:
Check_Online_Status();
sprintf(string,"  Read TO [%d,%d]? ",from,bh->Highest_Post);
PutStr(string);
sprintf(holder,"%d",bh->Highest_Post);
stat=LineInput(holder,string,5,120L);
if(stat==TIMEOUT) { return(stat); }
to=atoi(string);
if(to==0) { goto Read_Front1; }
if(to>bh->Highest_Post)
	{
    sprintf(string,"Too high. %d is the highest message\r\n",bh->Highest_Post);
	PutStr(string);
	goto Read_Front2;
	}
if(to<from)
	{
	sprintf(string,"Too low. %d is the lowest\r\n",from);
	PutStr(string);
	goto Read_Front2;
	}

return(Msg_Read(bh,from,to));
}

/****************************/
static int Msg_Immediate(bh)
	struct Board_Header *bh;
{
int from, stat;
char string[81], holder[6];

if(bh->Highest_Post==0)
	{
	PutStr("Immediage Read\r\n");
	PutStr("There are no messages in this base\r\n");
	return(SUCCESS);
	}

PutStr("Immediate read\r\n");

Immediate_Front1:
Check_Online_Status();
sprintf(string,"\r\nRead Which [1,%d]? ",bh->Highest_Post);
PutStr(string);
stat=LineInput("",string,5,120L);
if(stat==TIMEOUT) { return(stat); }
from=atoi(string);
if(from==0) { return(SUCCESS); }
if(from>bh->Highest_Post)
	{
    sprintf(string,"Too high. %d is the highest message\r\n",bh->Highest_Post);
	PutStr(string);
	goto Immediate_Front1;
	}
if(from<1)
	{
	sprintf(string,"Too low. 1 is the lowest message\r\n");
	PutStr(string);
	goto Immediate_Front1;
	}

return(Msg_Read(bh,from,from));
}

/****************************/
static int Msg_Delete_Frontend(bh)
	struct Board_Header *bh;
{
int from, to, stat;
char string[81], holder[6];

if(bh->Highest_Post==0)
	{
	PutStr("Delete Messages\r\n");
	PutStr("There are no messages in this base\r\n");
	return(SUCCESS);
	}

PutStr("Delete Messages\r\n");

Delete_Front1:
Check_Online_Status();
sprintf(string,"\r\nDelete FROM [1,%d]? ",bh->Highest_Post);
PutStr(string);
stat=LineInput("",string,5,120L);
if(stat==TIMEOUT) { return(stat); }
from=atoi(string);
if(from==0) { return(SUCCESS); }
if(from>bh->Highest_Post){
    sprintf(string,"Too high. %d is the highest message\r\n",bh->Highest_Post);
	PutStr(string);
	goto Delete_Front1;
}
Delete_Front2:
Check_Online_Status();
sprintf(string,"  Delete TO [%d,%d]? ",from,bh->Highest_Post);
PutStr(string);
stat=LineInput("",string,5,120L);
if(stat==TIMEOUT) { return(stat); }
to=atoi(string);
if(to==0) { goto Delete_Front1; }
if(to>bh->Highest_Post)
	{
    sprintf(string,"Too high. %d is the highest message\r\n",bh->Highest_Post);
	PutStr(string);
	goto Delete_Front2;
	}
if(to<from)
	{
	sprintf(string,"Too low. %d is the lowest\r\n",from);
	PutStr(string);
	goto Delete_Front2;
	}

return(Msg_Delete(bh,from,to));
}

/********************/
static int Between_Msg_Prompt(bh,bd,buf,number)
	struct Board_Header *bh;
	struct Board_Data *bd;
	char *buf;
	int number;
{
int ch, stat, command_accepted;
char str[133];

FOREVER
	{
	Check_Online_Status();
PutStr("\r\nA>gain N>ext L>ast Q>uit @>MainMenu, P>rivate + R>egular (Replies)\r\n");
	PutStr("D>elete ");
	if(User.Sec_Status>=100)
		{
		PutStr("+>Lock ->Unlock\r\n");
		}
	if(User.Sec_Status==255)
		{
		PutStr("E>xtract\r\n");
		}
	PutStr("\r\n");
	sprintf(str,"Message Option? ");
	PutStr(str);
	FOREVER
		{
		ch=ReadChar(120L);
		if(ch==TIMEOUT) { return(ch); }
		Check_Online_Status();
		switch(ch)
			{
			case 'A': /* AGAIN */
			case 'a':
				PutStr("\r\n");
				return(2); /* 2 := read again */
				break;
			case 'C': /* CONTINUE */
			case 'c':
			case 'N': /* NEXT */
			case 'n':
			case '\r':
				PutStr("\r\n");
				return(1); /* 1 := onward */
				break;
			case 'L': /* LAST */
			case 'l':
				PutStr("\r\n");
				return(3); /* 3 := backward */
				break;
			case 'D': /* DELETE */
			case 'd':
				if(User.Sec_Status<100 && User.Slot_Number!=bd->Person_Code)
					{
					command_accepted=FAILURE;
					break;
					}
				PutStr("Delete\r\n");
				Msg_Delete(bh,number,number);
				if(number>bh->Highest_Post) { return(0); }
				return(2);
				break;
			case '+': /* LOCK */
				if(User.Sec_Status<100){
					command_accepted=FAILURE;
					break;
				}
				PutStr("Lock\r\n");
				bd->Lock=1;
				Flush_Keys(bh);
				command_accepted=SUCCESS;
				break;
			case '-': /* UNLOCK */
				if(User.Sec_Status<100){
					command_accepted=FAILURE;
					break;
				}
				PutStr("UnLock\r\n");
				bd->Lock=0;
				Flush_Keys(bh);
				command_accepted=SUCCESS;
				break;
			case 'E': /* EXTRACT */
			case 'e':
				if(User.Sec_Status<255){
					command_accepted=FAILURE;
					break;
				}
				PutStr("Extract\r\n");
				stat=Msg_Extract(bh,bd,buf,number);
				command_accepted=SUCCESS;
				break;
			case 'P': /* PRIVATE MAIL */
			case 'p':
				if(User.Sec_Status<System.Mail_List->Write_Low
				||User.Sec_Status>System.Mail_List->Write_High){
					command_accepted=FAILURE;
					break;
				}
				PutStr("Private Reply\r\n");
				stat=Mail_Reply_To(System.Mail_List,
					bd->Person_Code);
				if(stat==SUCCESS)
					{
					Append_Stat(STAT_LETTER);
					++User.Mail_Sent;
					}
				command_accepted=SUCCESS;
				break;
			case 'R': /* REPLY */
			case 'r':
				if(bh->Highest_Post==bh->Maximum) { ch=1; }
				else { ch=0; }
				stat=Msg_Public_Reply(bh,bd);
				if(stat==SUCCESS)
					{
					Append_Stat(STAT_MESSAGE);
					++User.Messages_Posted;
					}
				if(stat==SUCCESS && ch==1) { return(2); }
				else { return(1); }
				break;
			case 'Q': /* QUIT */
			case 'q':
				PutStr("Quit\r\n");
				return(0); /* 0 := stop */
				break;
			case '@': /* MAIN MENU */
				PutStr("@ MainMenu\r\n");
				return('@');
				break;
			case '?': /* HELP */
			case '/':
				PutStr("Command summary\r\n");
				strcpy(str,System.Location);
				strcat(str,"BetweenMsg.Help");
				MenuSend(str);
				command_accepted=SUCCESS;
				break;
			case '\000': /* FN-KEY HIT */
				command_accepted=SUCCESS;
				PutStr("\r\n");
				break;
			default:
				PurgeLine();
				command_accepted=FAILURE;
				break;
			}
		if(command_accepted==FAILURE) { continue; }
		Check_Online_Status();
		break;
		}
	}
}

/********************/
static int Msg_Extract(bh,dh,buf,number)
	struct Board_Header *bh;
	struct Board_Data *dh;
	char *buf;
	int number;
{
int fd, stat, count;
char filename[133], final[133], string[81], *timestr;

PutStr("Extract\r\n");
sprintf(filename,"%s%s.%d",System.Location,"Extract",number);
Extract_Mark1:
Check_Online_Status();
PutStr("\r\nFile to save= ");
stat=LineInput(filename,final,132,120L);
if(stat==TIMEOUT) { return(TIMEOUT); }
if(stat==NO_CARRIER) { return(NO_CARRIER); }
if(stat==0) { PutStr("Abandoned\r\n"); return(SUCCESS); }

fd=open(final,O_WRONLY+O_CREAT+O_EXCL+O_APPEND);
if(fd==(-1))
	{
	PutStr(final);
	PutStr(" won't open. Try another,\r\nor a blank line to abandon\r\n");
	goto Extract_Mark1;
	}
/* SEND OUT HEADER INFO */
sprintf(string,"\nNumber: %hu of %hu\n",number,bh->Highest_Post);
write(fd,string,strlen(string));
if(strlen(dh->Title)>0)
	{
	sprintf(string," Title: %s\n",dh->Title);
	write(fd,string,strlen(string));
	}
sprintf(string,"Author: %s [%hu]\n",dh->Author,dh->Person_Code);
write(fd,string,strlen(string));
timestr=ctime(&dh->Time);
timestr[strlen(timestr)-1]='\0';
sprintf(string,"  Time: %s\n\n",timestr);
write(fd,string,strlen(string));

for(count=0;buf[count]!='\0';++count) { ; }
stat=write(fd,buf,count);
if(stat!=count) { PutStr("Error while writing\r\n"); }
stat=write(fd,"\n\n",2);
if(stat!=2) { PutStr("Error while writing\r\n"); }
stat=close(fd);
if(stat==EOF) { PutStr("Error while closing\r\n"); }

return(SUCCESS);
}

/********************/
static int Make_Open_Slot(bh)
	struct Board_Header *bh;
{
int x;
struct Board_Data *dh;

for(x=0;x<bh->Maximum;++x)
	{
	dh=bh->Start_of_Data;
	dh+=x;
	if(dh->Lock==0) { return(Msg_Delete(bh,x+1,x+1)); }
	}

return(FAILURE);
}

/********************/
static int Msg_Delete(bh,from,to)
	struct Board_Header *bh;
	int from,to;
{
struct Board_Data *dh, *dhsource, *dhreplace;
int count, offset;

/* CONVERT TO REAL DATA POSITIONS */
--from;
--to;

/* SET THE NEW HIGH MESSAGE */
bh->Highest_Post-=((to-from)+1);

/* MOVE THE HIGHER NUMBERED MESSAGES DOWN TO FILL THE DELETED GAP */
for(offset=0,count=to+1;count<bh->Maximum;++count,++offset)
	{
	dhreplace=bh->Start_of_Data;
	dhsource=bh->Start_of_Data;
	dhsource+=count;
	dhreplace+=(from+offset);
	*dhreplace=*dhsource;
	}

/* FINISH BY SETTING MESSAGES ABOVE THE HIGHEST TO 0L TIME */
for(count=bh->Highest_Post;count<bh->Maximum;++count)
	{
	dh=bh->Start_of_Data;
	dh+=count;
	dh->Time=0L;
	dh->Slot=0;
	}

return(Flush_Keys(bh));
}

/********************/
static int Msg_Write(bh)
	struct Board_Header *bh;
{
static struct Board_Data *data, the_struct;
int x, stat, msg_length;

PutStr("Write\r\n");

/* VERIFY FREE SPACE */
stat=FAILURE;
for(x=0;x<bh->Maximum;++x)
	{
	data=bh->Start_of_Data;
	data+=x;
	if(data->Lock==0) { stat=SUCCESS; break; }
	}
if(stat==FAILURE)
	{
	PutStr("No openings can be made for another message\r\n");
	return(FAILURE);
	}

data=&the_struct;

PutStr("\r\nA single 'Q' will quit\r\n");
PutStr("Enter a Title: ");
stat=LineInput("",data->Title,30,120L);
if(stat==TIMEOUT || stat==NO_CARRIER) { return(stat); }
if(stat==1 && toupper(data->Title[0])=='Q') { return(FAILURE); }

if(User.Sec_Status<100)
	{
	strcpy(data->Author,User.Name);
	}
else
	{
	PutStr("Name to use: ");
	stat=LineInput(User.Name,data->Author,30,120L);
	if(stat==TIMEOUT || stat==NO_CARRIER) { return(stat); }
	}

data->Person_Code=User.Slot_Number;
Check_Online_Status();

msg_length=Edit(Message_Buffer,2500,100,0);

if(msg_length==TIMEOUT || msg_length==NO_CARRIER) { return(msg_length); }
if(msg_length<0) { return(FAILURE); }

PutStr("Hold, saving your message\r\n");
return(Store_Message(bh,data,Message_Buffer));
}

/********************/
static int Msg_Public_Reply(bh,dh)
	struct Board_Header *bh;
	struct Data_Header *dh;
{
static struct Board_Data *data, the_struct;
int x, stat, msg_length;

if(User.Sec_Board<bh->Write_Low || User.Sec_Board>bh->Write_High)
   	{
	return(FAILURE);
	}

PutStr("Reply Publically\r\n");

/* VERIFY FREE SPACE */
stat=FAILURE;
for(x=0;x<bh->Maximum;++x)
	{
	data=bh->Start_of_Data;
	data+=x;
	if(data->Lock==0) { stat=SUCCESS; break; }
	}
if(stat==FAILURE)
	{
	PutStr("No openings can be made for another message\r\n");
	return(FAILURE);
	}

data=&the_struct;

PutStr("\r\nA single 'Q' will quit\r\n");
PutStr("Enter a Title: ");
stat=LineInput(dh->Title,data->Title,30,120L);
if(stat==TIMEOUT || stat==NO_CARRIER) { return(stat); }
if(stat==1 && toupper(data->Title[0])=='Q') { return(FAILURE); }

if(User.Sec_Status<100)
	{
	strcpy(data->Author,User.Name);
	}
else
	{
	PutStr("Name to use: ");
	stat=LineInput(User.Name,data->Author,30,120L);
	if(stat==TIMEOUT || stat==NO_CARRIER) { return(stat); }
	}

data->Person_Code=User.Slot_Number;
Check_Online_Status();

msg_length=Edit(Message_Buffer,2500,100,0);

if(msg_length==TIMEOUT || msg_length==NO_CARRIER) { return(msg_length); }
if(msg_length<0) { return(FAILURE); }

return(Store_Message(bh,data,Message_Buffer));
}
