/************************************************************************
 * This file contains infrequently used (or used once) routines for the
 * overlay version of The Metal/Z-MSG
 *
 *	Name: MEINFREQ.C
 *
 *		    Copyright (c) 1984, 1985, 1986  Tim Gary
 *			     All rights reserved.
 *
 ************************************************************************
 *
 *  1.50xx 08/13/86  Fixed for user.phone kludge
 *  1.50xx 03/26/86  Totalmsg count fixed in msgalert..
 *  1.50xx 03/17/86  Group stuff fixed?
 *  1.50xx 03/08/86  Read group file during message function..
 *  1.50xx 03/06/86  Faster summary file lookup method eanabled..
 *  1.50xx 03/01/86  Gearing up for groups..  Clear tagged/mail flags..
 *  1.40xx 02/28/86  msg[].reply form deleted..
 *  1.40xx 02/04/86  Hashed user file support added, display down/upload vals
 *  1.40xx 01/28/86  User city added back in..
 *  1.40xx 01/26/86  Mods for new user/message format..
 *  1.31a  10/13/85  Release version.  Changes:
 *		     Better help for user function.  Password length bug fixed.
 *  1.30xx 08/17/85  New inculde files and variable name bbs stuff..
 *  1.30xx 06/09/85  No more allocation for msg..
 *  1.30xx 05/25/85  Put msg allocation here also...(see also menter!)
 *  1.30xx 05/02/85  last_date/time extern defs deleted.
 *  1.30xx 03/03/85  Z3 Stuff looked into in this file.
 *  1.20b  01/18/85  Stats changes to conform to last_time, last_date.
 *  1.20b  01/09/85  Few more cosmetics..
 *  1.20b  01/07/85  Fixed misc routines, added count of msgs user's left..
 *  1.20a  11/11/84  Ok, final cosmetic touches and release for this
 *		     series of changes.
 *  1.20<test c> 11/09/84  Version message changed for new mes.c file.
 *  1.20<test b> 11/07/84  Lastread fixed.
 *  1.20<test a> 11/04/84  Version change, and additions to lastcalr file info.
 *  1.10e  11/03/84  Added more info to the '#' command.
 *  1.10e  11/02/84  Msg number=parent thing deleted. New put/get counters.
 *  1.10e  11/01/84  Untypo'd a typo.
 *  1.10e  10/31/84  Added stuff for private message counts/width stuff.
 *  1.10d  10/21/84  First time system setup bug (from 1.10a/b/c) fixed.
 *  1.10c  10/12/84  Routines added to verify parent/replys to msgs.
 *		     if not found in the table, then zero'd.
 *  1.10b  10/04/84  Message table build from message file corrected.
 *  1.10b  10/03/84  Version change, touch-ups, height properly initialized.
 *  1.10a  09/27/84  Cosmetic fixes (U command).
 *  1.10a  09/09/84  Cosmetic fixes.
 *  1.10a  08/31/84  Created.
 *
 ************************************************************************/

#include "xpm.h"	/* CPMIO header file for i/o operations.*/
#include "megen.h"	/* general defines		*/
#include "meglob.h"	/* global variable definitions	*/
#include "meovfn.h"	/* overlay function numbers	*/
#include "mefiles.h"	/* file names			*/

#include "ctype.h"

/**********************************************************
 * Overlay main program, dispatches to contained routines.
 **********************************************************/

ovmain(routine,parm)
 int routine,parm;
{
switch(routine)
	{
	case INIT:
		return init(parm);
	case MSGALERT:
		return msg_alert(parm);
	case READUSERS:
		return readusers(parm);
	case SAVEUSER:
		return saveuser(parm);
	case CALLS:
		return calls(parm);
	case STATS:
		return stats(parm);
	default:
		send("\nUnknown routine called for.\n");
		break;
	}
} /* ovmain */


/***************************
 * INITIALIZE EVERYTHING   *
 ***************************/

/* flag is argc and is used to tell if menter was run or not for getlastcalr */

init(flag)
 register int flag;
{
#ifdef Z3
if (O.ZCPR==3) *(z3env->wheel)=0;	/* turn off ZCPR3 wheel */
  else	/* fall through to next if */
#endif
 if (O.ZCPR) *O.SECURELOC=0;	/* if zcpr 1 or 2, turn off hazzards */

#ifdef MULTI_USER
#ifdef MPM
 if (strncmp(id_loc,ID_STR,3))
	{
	send("\n[Please wait]\n");
/* 	ovloader(OVMENTER,(char *)0,0);	won't work here..*//* not setup with lastcalr.. */
	}
#endif
#endif

printf("\n%s.\n%s\n",O.BBSNAME,O.VERSION);   /* display banner identifing the system */

/* done only if menter not used */

if (flag<=1) {
	gcntrs(0);
	getlastcaller();	/* get lastcalr file saved in menter.c */
	}

height=user.height;	/* reset height */

if (O.RTC) getdate();	/* get time and date from clock board */

/*
 * Find out if BYE is active and setup BYE parms..
 */

findbye();	/* Get bye, maxuser,tout, etc.. variables */

/*	setup timeout values for different people (different stats)  */
*tout=user.type_ptr->timeout;
*maxuser=user.type_ptr->maxuser;
*nulls=user.nulls;		/* setup users nulls	*/

if (msg==0)
	{
	register char *zero=0;
	send("\nAllocation ERROR: Memory full.\n");
	if (user.status==SYSOP)
		send("\n** Use the Configuration program and decrease total # of msgs **\n");
  	  else
		send("\n** Sorry, but the BBS is in need of repair.  Please try it later! **");
	hangup(YES);
	}

stats(0);	/* display user number/message stats */

flag=msg_alert(0);	/* check for msgs/build index */
putchar('\n');
return flag;
}	/* init */


/************************
 *  Get user from lastcalr file, file was created with menter
 ************************/

getlastcaller()
{
register FILE *lastcalr;
unsigned trec,tlast;
char tdate[9],ttime[9],*sp;
unsigned *uptr;

if ((lastcalr=open(LASTCALR,F_RD | F_UNLOCK))==NULL) 	/* open for read only */
	{
	send("\nERROR:  No LASTCALLER FILE!");
	exit();		/* exit to cpm in case it's sysop and can be fixed.. */
	}

#ifdef MULTI_USER

#ifdef MPM
  trec=bdos(153,0)%16;	/* get mp/m user console id */
#else
  trec=id_num;
#endif	 /* MP/M */

  setarec(lastcalr,trec);	/* seek to console position to get caller */

#endif	 /* Multi_User */

read(lastcalr,0);

sp=index(bufloc(lastcalr),0x1a)+1;	/* find ^Z and skip  */
uptr=sp;				/* point to values.. */
trec=uptr[0];
tlast=uptr[1];
strcpy(tdate,sp+4);
strcpy(ttime,sp+14);

dateasc(tdate,&last_date);
timeasc(ttime,&last_time);		/* convert back to internal format */
close(lastcalr);


users=open(USERFILE,F_RD | F_UNLOCK);	/* open for read */
setarec(users,trec);			/* seek to user */
read(users,0);				/* get the record */
movmem(bufloc(users),&user,128);	/* copy the record into our variable */
close(users);

user.number=trec;			/* since it's different in the file */
user.lastread=tlast;			/* get lastread back */
user.type_ptr=get_tp(user.status);	/* get pointer to type */
height=user.height;			/* get height setup  */
}


/************************************************
 * All new and improved msg summary builder...	*
 * Will use messages file alone and skip bad	*
 * sectors that it finds...			*
 ************************************************/

msg_alert(n)
 int n;
{
msg_record *mptr;	/* for easy access to values in the field */
register int flag;
int fileflag;		/* summary or message index build	  */
int lines;		/* line count of message */
int from_you;		/* count of messages left by you and still on sys */

from_you=flag=msgcount=privmsgs=mindex=fmsg=lmsg=0;
fileflag=TRUE;

if (!n) send("\n[Checking for your Mail]\n");	/* white lie, this is later */


/* First we read the groups file if there is one */

read_groups();


if ((summary=open(SUMMARY,F_RD | F_UNLOCK))!=NULL)
 	{					  /* summary file opened.. */
	toeof(summary);
	if (getrec(summary)<=1) close(summary);
	  else	{
		setarec(summary,1);
		fileflag=FALSE;
		}
	}
	else {		/* create summary to keep things happy */
		summary=open(SUMMARY,F_RW);
		sprintf(bufloc(summary),"0   \n");
		write(summary,1);
		close(summary);
		}

if (fileflag)	/* summary file not opened correctly, try messages */
	summary=open(MESSAGES,F_RD | F_UNLOCK);

#ifdef DEBUG
printf("\nBuilding message index from %s file.\n",fileflag ? "message" : "summary");
#endif

mptr=bufloc(summary);	/* setup buffer pointer	*/
while (summary && read(summary,1)==128)
	{
	msg[mindex].number=mptr->number;
	msg[mindex].seek=mptr->seek;
	msg[mindex].parent=mptr->parent;
	msg[mindex].group=mptr->group;
	msg[mindex].flags=(getrec(summary)-1)>>5;	/* set seek info */

	if (mptr->status==DEADMSG)
		msg[mindex].parent=0;

#ifdef DEBUG
printf("\nnum %u   seek %u   parent %u",mptr->number,mptr->seek,mptr->parent);
#endif

	if (mptr->lines && fileflag)
		{		/* message file skip text */
		if ( (mptr->status!=PRIVMSG && mptr->status!=NORMMSG
		    && mptr->status!=DEADMSG)
	    	    || mptr->seek!=(getrec(summary)-1) )
			{	/* not valid record, skip till one found.. */
			send("\n[Bad records found, skipping..]\n");
#ifdef DEBUG
printf("\nFile seek=%u, Message stat='%c' %x hex.",getrec(summary)-1,mptr->status,mptr->status);
#endif
			do lines=read(summary,1);
			  while (lines==128 && (mptr->status!=NORMMSG &&
				 mptr->status!=PRIVMSG && 
				 mptr->status!=DEADMSG) ||
				 mptr->lines>O.MLINES ||
				mptr->seek!=(getrec(summary)-1) );
			setrrec(summary,-1);
			if (lines==128) continue;	/* don't increment mindex, etc.. */		   else break;
			}
		}

	if (mptr->number)
		{
		lmsg=mptr->number;
		if (!fmsg) fmsg=lmsg;
		}

	if (mptr->status==PRIVMSG) privmsgs++;	/* count # of private msgs */

	if (!n && mptr->status!=DEADMSG && thisis(mptr->receiver))
		{	/* if message active and addressed to them */
		if (!flag)
			{
			send("\n[You have Mail!]\n\nNumber:  From:\n");
			++flag;
			}
		msg[mindex].flags|=MAIL;	/* init mail */
		printf("%5u    %s%s%s\n",
			mptr->number,mptr->sender,
			mptr->number>user.lastread ? "  [new]" : " ",
			mptr->status==PRIVMSG ? " [private]" : "");
		}

	if (mptr->status!=DEADMSG) {
			++msgcount;
			if (thisis(mptr->sender)) from_you++;
			}

	if (mptr->lines && fileflag)
		{
		lines=mptr->lines+1;
		read(summary,1);
		while (lines--) while (getc(summary)!='\n');  /* flush line */
		}

	++mindex;	/* killed msg in table too, for unkill,etc. */

	if (mindex>=O.MAXTOTMSGS)
		{
		printf("\nTo many msgs, (%d is max, including deleted ones)\n",
			O.MAXTOTMSGS);
		close(summary);
		return ERROR;
		}
	}	/* while */

if (!n && from_you) printf("\n[%d active messages left by you!]",from_you);
if (!n && !flag) send("\n[Sorry, you have no Mail]");

if (summary && fileflag)
	{
	send("\n[One moment please.  Be sure to do a message purge BEFORE anything else!]\n");
	for (flag=0; flag<mindex; flag++)	/* fix reply/parent conficts */
		if (getindex(msg[flag].parent)==ERROR) msg[flag].parent=0;
	}

close(summary);
nextmsg=lmsg+1;
totalmsgs=mindex;

return NULL;
} /* build index */


read_groups()
{
FILE *group_file;	/* text group file */
char t_str[MAXLINE+1];
n_groups=0;		/* global number */

if ((group_file=open(GROUPS,F_RD | F_UNLOCK))!=NULL && read(group_file,1)==128)
    {
    do	{
	*t_str='\0';
	fgets(t_str,group_file);
	while (isspace(*t_str)) sprintf(t_str,"%s",t_str+1);
	if (strlen(t_str)<6) break;	/* stop here */
	group[n_groups].read_flags=xtou(t_str);
	group[n_groups].name=malloc(strlen(t_str+5)+2);
	strcpy(group[n_groups].name,t_str+5);
	} while (++n_groups<N_GROUPS);
    close(group_file);
    }
}


/********************************
 * display users of the system	*
 ********************************/

readusers(n)
 register int n;
{
int theight;
char temp[NAMELEN+1];

ask("\nEnter full or partial Name, City\nor user number to start listing at? ",
temp,NAMELEN,UP);
if (n==PRINT)
	{
	theight=user.height;
	height=0;
	}
novhelp();
users=open(USERFILE,F_RD | F_UNLOCK);
if (isdigit(*temp)) setarec(users,atoi(temp)>0 ? atoi(temp)-1 : 0);
while (showuser(temp,n)!=ERROR);

if (users!=NULL) close(users);
if (n==PRINT) height=theight;
}


showuser(sstr,mode)
register char *sstr;
int mode;
{
char temp[120];
char tb[81];
usr *up;

up=bufloc(users);
if (read(users,1)!=128) return ERROR;

if (breakkey()) return ERROR;		/* stop? */
if (up->file_info&DEADUSER) return NULL;

sprintf(tb,"%s$$%s$$%s",up->name,up->city,up->phone);
upcase(tb);		/* make upper case only */

if (isdigit(*sstr))
	{
 	if (atoi(sstr) >= getrec(users)) return NULL;
	}
  else if (!sindex(sstr,tb) && *sstr!='\0') return NULL; /* no match-no show */

if (user.status==SYSOP)
	sprintf(temp," -- '%c' (%s) pw=%s  [%s]",up->status,
	up->status==SYSOP ? "sysop" :
		up->status==SPECIAL ? "special" :
			up->status==NORMAL ? "normal" :
				up->status==NOCPM ? "NO CP/M" :
					up->status==TWIT ? "*TWIT*" :
						"OTHER",up->pass,up->phone);
   else { mode=NOPRINT; *temp='\0'; }
sprintf(buffer,"\n%-5u %s %s  From %s",getrec(users),up->name,temp,up->city);

if (send(buffer)==ERROR) return ERROR;
if (mode==PRINT) {
		print(buffer);		/* send to list device */
		print("\r");
		}

sprintf(buffer,"\n\\---- Called %u time(s).  Last message was %u on %s %s%s\n",
	up->calls,up->lastread,ascdate(up->date),
	O.RTC!=NOCLOCK ? "at " : "",O.RTC!=NOCLOCK ? asctime(up->time) : "");

if (send(buffer)==ERROR) return ERROR;
if (mode==PRINT) {
		print(buffer);		/* send to list device */
		print("\r");
		}
 return NULL;	/* alls done */
}


/*******************************
 * display the user on console *
 *******************************/

disuser()
{
printf("\nEnter the number next to what you wish to change:\n");

sprintf(buffer,"\
\n(1) Experience=%s (2) Bell=%s   (3) Nulls=%d\
\n(4) %s         (5) %s messages when first entering the bbs.\
\n(6) Change Password.  (7) Terminal height=%d (if 0, no pause)\
\n(8) Terminal width=%d\n",
	(user.flags&EXPERT) ? "EXPERT" : "NOVICE",
	(user.flags&BELL) ? "ON" : "OFF",(int)user.nulls,
	(user.login) ? "Skip BBS " : "Enter BBS",
	(user.flags&READNEW) ? "Read" : "Don't read",
	user.height,user.width);
send(buffer);

}


/********************
 * Save users stuff *
 ********************/

saveuser(n)
 register unsigned n;
{
char temp[10];

disuser();	/* display the user */

  do {
     if (user.flags&EXPERT) sprintf(buffer,"\nChange :");
	else sprintf(buffer,"\nEnter number to change (Return to End, '?' to redisplay menu) :");
     ask(buffer,temp,9,UP);
     switch (*temp)
	{
	case '1':	user.flags^=EXPERT;	/* XOR toggle */
			if (user.flags&EXPERT) send("\n[Expert mode]");
			  else send("\n[Novice mode]");
			break;

	case '2':	user.flags^=BELL;
			if (user.flags&BELL) send("\n[Bell ON]");
			  else send("\n[Bell OFF]");
			break;

	case '3':	ask("\nNumber of nulls (0-9)? ",buffer,5,UP);
			if ((*temp>='0') && (*temp<='9') && *buffer!='\0')
				{ *nulls=*buffer-'0';
 				  user.nulls=*buffer-'0';	}
			  else send("\n[Nulls NOT changed]");
			break;

	case '4':	if ( !(user.type_ptr->maxuser) )
				{
				send("\nYou don't have access to the Operating System...");
				break;
				}
			user.login^=1;		/* for now.. */
			if (!user.login) send("\nYou will now enter the message system when you call.");
			  else send("\nYou will now enter the Operating System instead of the BBS when you call.");
			break;

	case '5':	user.flags^=READNEW;
			if (user.flags&READNEW)
			    send("\n[AUTO read of messages ON]");
			  else send("\n[AUTO read of messages OFF]");
			break;

	case '6':	do
			  ask ("\nEnter new password, or RETURN if no change -> ",buffer,PASSLEN+2,UP+NOECHO+NUMBER);
			  /* no spaces, or number for first char */
			while (isspc(buffer) || isdigit(*buffer));
			if (strlen(buffer)>PASSLEN)
			   {
			   buffer[PASSLEN]='\0';
			   printf("\nWARNING:  Password truncated to %d characters.\n",PASSLEN);
			   }
			if (*buffer!='\0')
				{
				ask("\nNow enter it again for verification -> ",buffer+50,PASSLEN,UP+NOECHO+NUMBER);
				if (!strcmp(buffer,buffer+50)) 
					strcpy(user.pass,buffer);
				  else send("[Incorrect. Password was NOT changed]\n");
				}
			  else send("\n[Password NOT changed]");
			break;

	case '7':	ask("\nTerminal Height (0=no pause)? ",buffer,5,UP);
			if (*buffer!='\0')
				user.height=height=atoi(buffer);
			  else send("\n[Height not changed]");
			break;

	case '8':	ask("\nTerminal width? ",buffer,5,UP);
			if (*buffer!='\0')
				{
				if (atoi(buffer)<20)
					send("\n[Must be >=20, unchanged]");
				   else user.width=atoi(buffer);
				}
			  else send("\n[Width not changed]");
			break;

	case '\0':	break;
	case '?':	send("\nChoose the number of the item you wish to change\nfrom the following selections.\n");
			disuser();
			break;
	default:	send("\n[Invalid option]");
	  }	/* switch */
    } while (*temp!='\0');

ask("\nMake changes permanent for future calls (y/n)? ",temp,2,UP);
if (*temp=='Y')
	{
	char temp_time[2],temp_date[3];
	send("\n[Saving]");
	n=user.lastread;
	user.lastread=nextmsg ? nextmsg-1 : 0;
	movmem(user.time,temp_time,2);
	movmem(user.date,temp_date,3);
	movmem(time,user.time,2);
	movmem(date,user.date,3);
	putuser(user.number);	/* write new user info back to file */
	movmem(temp_time,user.time,2);
	movmem(temp_date,user.date,3);
	user.lastread=n;	/* restore last read msg number */
	}
}	/* saveuser */


/* write user specified to correct place in users file, 0=write to current
   possition in the OPEN users file */ 

putuser(un)
register unsigned un;
{
register usr *up;
register int flag;

if ((users=open(USERFILE,F_RW | F_LOCK))==NULL) return ERROR;   /* else, open it */
up=bufloc(users);
setarec(users,un);
read(users,0);
user.number=up->number;
movmem(&user,bufloc(users),128);	/* get user vars */
user.number=un;
write(users,0);	/* write record, no increment... */
close(users);		/* either method called closes the users file */

return NULL;		/* NULL=all's well */
}


/*************************
 * Display message stats *
 *************************/

stats(n)
{
sprintf(buffer,"\nYou're caller number %u.\n\
You've called %u time(s), last one being on %s%s%s.\n\n",callnum,user.calls,
ascdate(last_date),O.RTC!=NOCLOCK ? " at " : "",
O.RTC!=NOCLOCK ? asctime(last_time) : "");

if (send(buffer)==ERROR) return;

if (user.uploads || user.downloads)
	{
	sprintf(buffer,"You have downloaded %u files, and uploaded %u.\n\n",
		user.download,user.uploads);
	if (send(buffer)==ERROR) return;
	}

sprintf(buffer,"There are %d active messages.",msgcount);
if (send(buffer)==ERROR) return;

if (privmsgs) sprintf(buffer," (%d are private)",privmsgs); else *buffer='\0';
if (send(buffer)==ERROR) return;

sprintf(buffer,"\nYour last read message was %u.\n\
The current high message is %u.\n",user.lastread,nextmsg ? nextmsg-1 : 0);
send(buffer);
}


/*********************
 * List callers file *
 *********************/

calls(n)
{
if (type(CALLERS)!=ERROR)		/* type the callers of today */
  if (user.status==SYSOP)
	{
	ask("[Purge callers file?]",buffer,2,UP);
	if (*buffer=='Y')
		{
		send("[Purging]");
		unlink(CALLERS);
		}
	}
}


/* End of File */


