/******************************************************************************
 *	METAL Message section...
 *
 *	File name: MES.C
 *
 *	       Metal and Metal Message System are Trademarked and
 *		    Copyright (c) 1984, 1985, 1986 Tim Gary
 *			       All rights reserved.
 *
 *
 *   This module contains common message base routines (read, msgheader, etc)
 *
 *****************************************************************************
 *
 * 1.50xx 03/14/86 Split the message header to avoid overflow.
 * 1.50xx 03/10/86 Cosmetics to read header format..
 * 1.50xx 03/08/86 Header function updated to allow groups..
 * 1.50xx 03/04/86 Support for date/time read for message header..
 * 1.50xx 03/01/86 msgheader parms changed.. Tagged and Mail messages..
 * 1.40xx 02/28/86 Started removal of msg.reply value..  Use msg_reply subr.
 * 1.40xx 02/10/86 Put help in library help file..
 * 1.40xx 01/26/86 Fixed for new time/date and other stuff..
 * 1.31a  10/13/85 Release version.  Fixed bug in getheader(), moved read
 *  		   message functions to overlay.
 * 1.30xx 6/23/85  Spelling error fixed.
 * 1.30xx 3/03/85  Z3 stuff added, fixed prompts for ignoring controls.
 * 1.20b 01/21/85  Message read fixed for sysop '<' function..
 * 1.20b 01/11/85  Swap order of sender/recipient in summary.
 * 1.20b 01/09/85  Really fixed search function.  Added & op to search.
 *		  Added sysop link of a message to a file..
 * 1.20b 01/07/85  Search function fixed, Quiet read mode, novice help,
 *		  extended abort checking during search operations, and
 *		  summary function output changed.
 * 1.20a 11/11/84  Nothing found message fixed in selective read.
 * 1.20<test c> 11/09/84  [Nothing found/not addressed to ya/dead] msg in read.
 * 1.20<test a> 11/04/84  Fixed timefix routine for no clock.
 * 1.10e 11/01/84  Allow for reading of dead msgs.
 * 1.10e 10/31/84  Width stuff changed in msgheader, fixed for unkill.
 * 1.10c 10/12/84  Made a few functions here overlays to conserve space.
 * 1.10b 10/09/84  Complete reworking of read message routines (internal only).
 * 1.10b 10/04/84  More cosmetics.. Should be it for a while.
 * 1.10a  9/28/84  Fixed reply problem (msg shown after reply..).
 * 1.10a  9/26/84  Added a few cosmetic changes (y/n)? and selective read help.
 * 1.10a  9/24/84  Fixed edit user/kill this msg, bugs.. (introduced 8/31/84)
 * 1.10a  9/12/84  Eliminated Reverse read bug (replies not listed)
 * 1.10a  9/12/84  Cosmetic changes.
 * 1.10a  9/09/84  Cosmetic changes.
 * 1.10a  9/07/84  Added Kill previous message to selective read for sysop.
 * 1.10a  9/06/84  Changed Edit/delete user on read for previous message.
 * 1.10a  8/31/84  Split for overlays, now contains readmsg() and commons.
 *
 * 1.01a  8/05/84  Got things going, added delete user, and edit user during
 *		  message read (prompted mode).
 * 1.01a  7/14/84  Message editing bugs fixed (delete line.. movmem changed).
 *		   Added subject truncation notice on message entry.
 * 1.01a  6/30/84  Cleaned up more functions.
 * 1.01a  6/26/84  Cleaned up a few functions, and added MULTI_USER stuff.
 * 1.01a  6/10/84  Fixed to work with Aztec C 1.06. Started Multi-user stuff.
 *
 * 1.0b   4/20/84  (cont) Added msearch, for string search for msgs..
 * 1.0b	  4/13/84  Modified for upper/lower case names, and added check for
 *		maximum messages allowed (killed and active ones).
 *
 * 1.0	  1/20/84  New format of Message file headers.
 * p3.0	 12/14/83  pre-release version 3.0 mods for MCONFIG..
 *
 *****************************************************************************/

#include "xpm.h"	/* i/o operations related defs	*/
#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"

#define	SRCHHELP "MSGSRCH HLP"

char msearch();

getindex(m)
 register unsigned m;
{
register int a;

/* a rather long/cryptic statement..  if m!=0, then loop til m is found */
 if (m) for (a=0; a<mindex; a++) if (msg[a].number==m) return a;

return ERROR;	/* if we are here, then it wasn't found in the loop */
}


/* return reply to message given, as long as it is greater than
   gt_num	*/

unsigned msg_reply(number,gt_num)
 unsigned number,gt_num;
{
register int i;

for (i=0; i<mindex; i++)
	if (msg[i].parent==number && msg[i].number>gt_num)
		return msg[i].number;

return 0;	/* if we are here, it wasn't found */
}


#ifdef MULTI_USER

/*********************************************************
 * Check for new messages, update counters/array if some
 * are actually found.  Used only in Multi-user systems
 *********************************************************/

mu_update()
{
register unsigned tn_msg;
register msg_record *mptr;
register int rval=0;		/* return value # of new msgs */
unsigned new_msgs();

if (!(tn_msg=new_msgs())) return rval;	/* check if counters updated */

/* tn_msg will contain # of new 'nextmsg' counter, from counters file */

printf("\n%u new messages entered by other users.\n",tn_msg-nextmsg);
summary=open(SUMMARY,F_RD | F_UNLOCK);
mptr=bufloc(summary);
toeof(summary);	setrrec(summary,nextmsg-tn_msg);	/* backup # msgs */
rval=tn_msg-nextmsg;

while ((nextmsg++)<tn_msg)	/* and here we read new stuff */
	{
	read(summary,1);
	if (mptr->status!=DEADMSG)
		{
		msg[mindex].number=mptr->number;
		msg[mindex].seek=mptr->seek;
		msg[mindex].parent=mptr->parent;
		++totalmsgs;  ++msgcount;
		if (thisis(mptr->reciever))
			printf("\n#%u from %s is for you.\n",mptr->number,
				mptr->sander);
		}  /* if */
	}  /* while */
close(summary);
lmsg=msg[mindex-1].number;
putchar('\n');

return rval;	/* # of new msgs */
}  /* mu_update */


/*********************************************************
 * check for differing (in nextmsg) counters file
 * returns new nextmsg
 *********************************************************/

unsigned new_msgs()
{
register FILE *counters;
unsigned tnext=0,*uptr;

if ((counters=open(COUNTERS,F_RD | F_UNLOCK))!=NULL)
	{
	uptr=bufloc(counters);
	tnext=uptr[2];	/* next message */
/*	sscanf(bufloc(counters),"%*d %*d %d",&tnext);	*/
	close(counters);
	if (tnext==nextmsg) tnext=0;
	}
return tnext;
}

#endif /* multi user */



/****************************************************************
 * format message header info 					*
 * formats.  0=Quick Summary format    1=Full summary format	*
 *           2=Read Message format     3=Kill message format	*
 *	     4=unkill..						*
 ****************************************************************/

get_header(mode,buf)
 register int mode;
 register char *buf;
{
char tstr[30];
register int a;

*tstr='\0';

/*  if (message.parent) 
	if ( getindex(message.parent)==ERROR ) message.parent=0;	*/

if (!mode)
	sprintf(buf,"\n%u  %s",message.number,message.topic);
   else if (mode==1)
	{
	if (message.parent) sprintf(tstr,"[R/%u]",message.parent);
	*(message.date_entr+5)='\0';		/* chop off year... */
	sprintf(buf,"\n%4u %s %s%s \"%s\"(%d)  To: %s %s%sFrom: %s",
		message.number,ascdate(message.date_entr),
		(msg[getindex(message.number)].flags & TAGGED) ? "[t] " : "",
		tstr,message.topic,message.lines,message.receiver,
		(message.status==PRIVMSG ? "<Priv>" :
		    message.status==DEADMSG ? "<Dead>" : ""),
		user.width<80 ? "\n    " : "  ",message.sender);
	if (user.width>=80)
		buf[user.width]='\0'; /* cut off at 80 chars */
	}
     else
	if ((mode!=3 && mode!=4) || !(user.flags&EXPERT) )
	  {
	  char tstr2[50],tstr3[15];
	  if (message.parent) sprintf(tstr,"  [reply to #%u]",message.parent);
	  if (message.status==PRIVMSG) strcpy(tstr3,"private");
		else if (message.status==DEADMSG) strcpy(tstr3,"deleted");
			else *tstr3='\0';
	  if (message.date_read[0]!=0x00)
		sprintf(tstr2,"  [%s%sread %s%s%s]",
			tstr3,*tstr3 ? " - " : "",
			ascdate(message.date_read),
			O.RTC ? " at " : "",
			O.RTC ? asctime(message.time_read) : "");
	    else if (*tstr3) sprintf(tstr2,"  [%s]",tstr3);
		    else *tstr2='\0';
	  sprintf(buf,"\n-------\nMessage #%u  ** %s **%s\
\nPosted: %s %s%s\
\n  From: %s",message.number,
message.group<n_groups ? group[message.group].name : "",
tstr,ascdate(message.date_entr),O.RTC ? "at " : "",
O.RTC ? asctime(message.time_entr) : "",message.sender);

	  sprintf(buf+strlen(buf),"\
\n    To: %s%s\
\n About: %s (%d lines) %s\n\n",
message.receiver,tstr2,message.topic,message.lines,
(msg[getindex(message.number)].flags & TAGGED) ? "  [tagged]" : "");
	  }
}

/*  Format of read header:

-------
Message #465 posted 01/12/34 at 12:00 pm  (reply to #123)
 From: Tim Gary
   To: Sheep Dip  [read 01/13/86 at 10:54 pm]
About: Testing new header (50 lines)  [tagged]

-------
Message #465  ** General News **  [reply to #123]
Posted: 01/12/34 at 12:05 am
  From: Tim Gary
    To: Sheep Dip  [private - read 01/23/45 at  6:12 am]
 About: Testing new header (50 lines)  [tagged]

*/

/* msgheader() displays the message header information in one of three
 * formats.  0=Quick Summary format    1=Full summary format
 *           2=Read Message format     3=Kill message format
 *	     4=Unkill msg
 * Bit READ_KIL of mode indicates display even if dead.
 * It expects you to supply the file descriptor, a 'starting' msg number (which
 *	is only used by the summary funtions, and should be 1 or 0 for Read),
 *      and the mode as described above.
 *
 * it returns ERROR if it could not read the header, or ^K was entered aborting
 *  its output.
 * NULL (0) is returned if the message is private, and can't be read by the
 *  current user of the system.
 * if everything is ok, it returns the number of lines in the message.
 */

msgheader(ffd,startmsg,mode)
 FILE *ffd;
 unsigned startmsg;
 int mode;
{
register int fixed_mode;

fixed_mode=mode&0x0f;

if (read(ffd,1)!=128) return ERROR;
movmem(bufloc(ffd),&message,128);	/* get msg structure	*/

if (message.status==DEADMSG) message.parent=0;

if (fixed_mode==4)
	{
	if (message.status!=DEADMSG)
		{
		if (startmsg!=0) printf("\n[Message %u not dead!] ",message.number);
		return NULL;
		}
	}
  else if ((message.status==DEADMSG) && !(mode & 0x80)) return NULL;

if ((message.status==PRIVMSG || (message.status==DEADMSG && (mode & READ_KIL)))
	 && !thisis(message.receiver)
	 && ( (fixed_mode<3)
		? (!(user.type_ptr->privflags & READPRIV))
		: (!(user.type_ptr->privflags & KILLFLAG)) )
	 && (ustrcmp(message.sender,user.name)) )
	return NULL;

if (fixed_mode==3 && !thisis(message.receiver) &&
	ustrcmp(message.sender,user.name) &&
	!(user.type_ptr->privflags & KILLFLAG)) return NULL;

if (message.group<n_groups)
	if (!(group[message.group].read_flags & user.group_flags))
		return NULL;	/* Doesn't belong to group */

/* see if this needed : if (message.number>=startmsg || startmsg==0) 	
	{	*/

	if (msearch(0)==0) return NULL;	/* no match.. ret */

	get_header(fixed_mode,buffer);
	if ((fixed_mode!=3 && fixed_mode!=4) || !(user.flags&EXPERT) )  /* write line */
		if (send(buffer)==ERROR) return ERROR;

/*	}
	else return NULL;	*/

return message.lines ? message.lines : 1;	/* if 0 lines, fake it.. */
}


search_help()		/* display help for search functions */
{
ltype(METHELP,SRCHHELP);
}


/************************************************************************
 *  Search for string in message variables if passed string ptr=0,
 *  else, setup the string to be compared to the message variables later.
 *  string, if passed, format of:
 *	<s|d|f|t|*>:<search string>	s or g=subject/group search
 *		f=from search		t=to search	d=date search
 *		*=search all above fields	&=perform 'and' search on
 *  examples:					  ALL fields...sorry
 *	s:for sale		ft:tim gary		d:09-02-84
 *	t:all &s:for sale	t:tim gary s:metal
 *	g:general
 *  in read operations:
 *	rs;30+ s:wanted		r;223+ *:sysop    etc...
 *
 *  returns:	bit 0 ON for SUBJECT match	bit 1 ON for DATE match
 *		bit 2 ON for FROM match		bit 3 ON for TO match
 *		bit 8 ON if no search..
 ************************************************************************/

char msearch(sstr)
 char *sstr;
{
char *ps,*t_sstr;
register char ret_flag;

static char f_pattern[NAMELEN+1];
static char t_pattern[NAMELEN+1];
static char s_pattern[TOPICLEN+2];
static char d_pattern[10];

static int field_flag,and_flag;

ret_flag=0;

if (sstr)	/* if not null ptr, setup search string */
	{
	(*t_pattern)=(*f_pattern)=(*s_pattern)=(*d_pattern)='\0';  /* make empty at first */
	field_flag=and_flag=NO;	/* all off */
	for (t_sstr=sstr; (ps=index(t_sstr,':')) && (t_sstr!=1); t_sstr=index(t_sstr,':')+1)
	  /* '=' IS ON PURPOSE!!!!!!!!	*/
		{
		while (--ps>=t_sstr)	/* backwards parse from ':'	*/
			{
			switch (*ps) {
				case 'S':
				case 'G':	/* group too. */
					field_flag|=1;
					get_pat(ps,s_pattern,TOPICLEN);
					break;
				case 'D':
					field_flag|=2;		/* DATE */
					get_pat(ps,d_pattern,8);
					break;
				case 'F':
					field_flag|=4;
					get_pat(ps,f_pattern,NAMELEN);
					break;
				case 'T':
					field_flag|=8;
					get_pat(ps,t_pattern,NAMELEN);
					break;
				case '&':
					and_flag=YES;
					break;
				case '*':
					field_flag=15;   /* all bits on */
					get_pat(ps,t_pattern,NAMELEN);
					get_pat(ps,f_pattern,NAMELEN);
					get_pat(ps,d_pattern,8);
					get_pat(ps,s_pattern,TOPICLEN);
					break;
				default:
					ps=t_sstr;	/* stop loop here */
				} /* switch */
			} /* while loop */
		} /* for loop */
	ret_flag=field_flag;
	} /* main 'string passed' if */

  else {	/* no string passed, compare with message fields */
	if (!field_flag) ret_flag=128;	/* flag bit 7 if no compare */
	   else	{
		char tstr[MAXLINE+1];	/* used for sender string */
		if (field_flag&1)
			{
			ret_flag|=(usindex(s_pattern,message.topic) ? 1 : 0);
			if (message.group<n_groups)
				ret_flag|=(usindex(s_pattern,group[message.group].name) ? 1 : 0);
			}
		if (field_flag&2)
			ret_flag|=(usindex(d_pattern,ascdate(message.date_entr)) ? 2 : 0);
		if (field_flag&4)
			ret_flag|=(usindex(f_pattern,message.sender) ? 4 : 0);
		if (field_flag&8)
			ret_flag|=(usindex(t_pattern,message.receiver) ? 8 : 0);
		if (and_flag && (field_flag!=ret_flag)) ret_flag=0; /* not & */
		} /* check pattern?? if */
	}

return ret_flag;	/* return value */
}	/* msearch */


get_pat(sub_str,dest,max_len)
 char *sub_str,*dest;
 int max_len;
{
register char *tp2;
register char *tp1,*s_cop;

tp1=s_cop=index(sub_str,':')+1;

if (tp2=index(tp1,':'))		/* if there are more ':'s */
	while (!isspace(*(--tp2)) && tp2>tp1);
  else tp2=tp1+strlen(tp1);
--tp2;

while (tp1<=tp2 && (tp1-s_cop)<=max_len) *(dest++)=*(tp1++);
*dest='\0';	/* terminate */

} /* get_pat */


/* End of MES.C */


