/* The following code will broadcast 'Mail for:' beacons
 * for private users with unread mail on a regular interval
 *
 * Also contains the commands to set the R: header read options
 * with the 'bulletin' command.
 *
 * original: 920306
 * rewritten: 920326
 * Copyright 1992, Johan. K. Reinalda, WG7J/PA3DIS
 * Permission granted for non-commercial use only!
 */
#include "global.h"
#include "commands.h"
#ifdef MSDOS
#include "socket.h"
#endif
#include "files.h"
#include "dirutil.h"
#include "bm.h"
#include "x.h"


#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: mailfor.c,v 1.33 2001/05/06 16:32:57 brian Exp $";
#endif

#ifdef MAILFOR
#ifdef AX25

#define MAXMFLEN 256
static struct timer Mftimer;
static char ax_mftext[MAXMFLEN + 1] = "Mail for:";

#define DEFMFLEN 9
static int mflen = DEFMFLEN;		/*Initial lenght of mail-for string*/

#ifdef HOLDMONITOR
#ifdef SOUNDS
char *HOLDsound;
#endif
int HOLDmode, HOLDindicator;
#endif

#ifdef ALERTMONITOR
#ifdef SOUNDS
static char *ALERTsound;
#endif
static int ALERTmode;
int ALERTindicator;
#endif

extern int CountConfUsers (void);
static int checknewmail (char *area);
static int mf_exclude (char *name);
static void countMessages (void);
static int setmailfor (void);
static void ax_mf (struct iface * ifp);
static void Mftick (void *v);
int dombmailfor (int argc, char **argv, void *p);
int indexFwdBbs (char *name);


static struct no_mf *No_mf = NULLMF;

#ifdef ALERTMONITOR
static struct mfalert *mf_alerts = NULLMFA;
#endif


/*Read a private message area's control file, searching for unread mail
 *this is indicated by the status int
 */

static int
checknewmail (char *area)
{
FILE *fp;
char mailbox[100];
long size;
int i, nmsgs;
struct let *cmsg;
struct let *new;
int ret = 0;

	sprintf (mailbox, "%s/control/%s.ctl", Mailspool, area);
	if ((fp = fopen (mailbox, READ_BINARY)) != NULLFILE) {
		size = (long) filelength (fileno (fp));
		new = (struct let *) mallocw ((unsigned int) size);
		kwait (NULL);
		(void) fread (new, (unsigned int) size, 1, fp);
		(void) fclose (fp);
		kwait (NULL);
		nmsgs = (int) (size / (long) sizeof (struct let));

		for (cmsg = new, i = 0; i < nmsgs; i++, cmsg++) {
			if (!(cmsg->status & BM_READ)) {
				ret = 1;
				break;
			}
		}
		free (new);
	}
	return ret;
}


/* Check name with exclude list;
 * returns 1 if found, 0 if not
 */
static int
mf_exclude (char *name)
{
struct no_mf *nm;

	/*Now check the 'exclude' list*/
	for (nm = No_mf; nm != NULLMF; nm = nm->next) {
		if (!stricmp (nm->area, name))
			return 1;
	}
	return 0;
}


#ifdef CONVERS
static char ActiveMessages[] = "Active Messages: %ld  (%s in conference sessions)";
#else
static char ActiveMessages[] = "Active Messages: %ld";
#endif
static long ACTIVEMessages = 0;

static void
countMessages (void)
{
char buf[128];
struct ffblk ff;
long size;

	ACTIVEMessages = 0;
	sprintf (buf, "%s/control/*.ctl", Mailspool);
	if (findfirst (buf, &ff, 0) == 0) {
		do {
			kwait (NULL);	/* Let others run */
			sprintf (buf, "%s/control/%s", Mailspool, ff.ff_name);
			size = fsize (buf);
			ACTIVEMessages += (size / 14L);
		} while (findnext (&ff) == 0);
	}
}


static int
setmailfor (void)
{
char buf[80];
struct ffblk ff;
char *cp;

	sprintf (buf, "%s/*.txt", Mailspool);
	if (findfirst (buf, &ff, 0) == 0) {
		do {
			kwait (NULL);	/* Let others run */
			cp = strrchr (ff.ff_name, '.');
			if (cp)		/* should always be true */
				*cp = '\0';
			/*must be private mail area, and not on exclude list !*/
			if (!issysarea (ff.ff_name) && !mf_exclude (ff.ff_name)) {
				if ((strlen (ax_mftext) + strlen (ff.ff_name)) > MAXMFLEN - 1)
					break;	/* That's all folks */
				if (checknewmail (ff.ff_name)) {
					strcat (ax_mftext, " ");
					strcat (ax_mftext, ff.ff_name);
				}
			}
		} while (findnext (&ff) == 0);
	}
	countMessages ();

	return (int) strlen (ax_mftext);
}


/*This is the low-level broadcast function.*/
static void
ax_mf (struct iface *ifp)
{
struct mbuf *bp;
#ifdef CONVERS
int inconference = 0;
char fakestr[8];
#endif

	/* prepare the header */
	if ((bp = alloc_mbuf ((int16) mflen)) == NULLBUF)
		return;

	/*copy the data into the packet*/
	bp->cnt = (int16) mflen;
	memcpy (bp->data, ax_mftext, (unsigned int) mflen);

	/* send it */
	(void) (*ifp->output) (ifp, Ax25multi[MAILCALL], ifp->hwaddr, PID_NO_L3, bp);


	/* prepare the header */
	if ((bp = alloc_mbuf ((int16) (strlen (ActiveMessages) + 16))) == NULLBUF)
		return;

	/*copy the data into the packet*/
#ifdef CONVERS
	inconference = CountConfUsers ();
	sprintf ((char *) bp->data, ActiveMessages, ACTIVEMessages, (inconference) ? itoa (inconference, fakestr, 10) : "NONE");
#else
	sprintf (bp->data, ActiveMessages, ACTIVEMessages);
#endif
	bp->cnt = (int16) strlen ((char *) bp->data);

	/* send it */
	(void) (*ifp->output) (ifp, Ax25multi[MAILCALL], ifp->hwaddr, PID_NO_L3, bp);
}


static void
Mftick (void *v OPTIONAL)
{
struct iface *ifp = Ifaces;

	stop_timer (&Mftimer);	/* in case this was 'kicked' with a 'mailfor now'*/

	/* Now find private mail areas with unread mail. add these to the info-line */
	ax_mftext[DEFMFLEN] = '\0';	/* Back to only 'Mail for:' again*/

	if ((mflen = setmailfor ()) < DEFMFLEN + 1) {
		start_detached_timer (&Mftimer);
		return;		/* No unread mail */
	}

	/*broadcast it on all configured interfaces*/
	for (ifp = Ifaces; ifp != NULL; ifp = ifp->next)
		if (ifp->flags & MAIL_BEACON)
			ax_mf (ifp);
	/* Restart timer */
	start_detached_timer (&Mftimer);
}


#ifdef ALERTMONITOR
static int domfalert (int argc, char *argv[], void *p);
#endif
static int domfexclude (int argc, char *argv[], void *p);
#ifdef HOLDMONITOR
static int domfhold (int argc, char *argv[], void *p);
#endif
static int domfnow (int argc, char *argv[], void *p);
int domflist (int argc, char *argv[], void *p);


static struct cmds MFORtab[] =
{
#ifdef ALERTMONITOR
	{ "alert",	domfalert,	0, 0, NULLCHAR },
#endif
	{ "exclude",	domfexclude,	0, 0, NULLCHAR },
#ifdef HOLDMONITOR
	{ "hold",	domfhold,	0, 0, NULLCHAR },
#endif
	{ "listing",	domflist,	0, 0, NULLCHAR },
	{ "now",	domfnow,      256, 0, NULLCHAR },
	{ "timer",	domftimer,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};


int
dombmailfor (int argc, char *argv[], void *p)
{
	return subcmd (MFORtab, argc, argv, p);
}


int
domflist (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	if (mflen > DEFMFLEN)
		tprintf ("%s\n", ax_mftext);
	return 0;
}


int
domftimer (int argc, char *argv[], void *p OPTIONAL)
{
#ifdef CONVERS
int inconference = 0;
char fakestr[8];
#endif

	if (argc < 2) {		/* set the timer */
		tprintf ("Mail-for timer: %lu/%lu\n", read_timer (&Mftimer) / 1000L,
			 dur_timer (&Mftimer) / 1000L);
		if (ACTIVEMessages) {
#ifdef CONVERS
			inconference = CountConfUsers ();
			tprintf (ActiveMessages, ACTIVEMessages, (inconference) ? itoa (inconference, fakestr, 10) : "NONE");
#else
			tprintf (ActiveMessages, ACTIVEMessages);
#endif
			tputs ("\n");
		}
		if (mflen > DEFMFLEN)
			tprintf ("%s\n", ax_mftext);
		tputs ("\n");
		return 0;
	}
	Mftimer.func = (void (*)(void *)) Mftick;	/* what to call on timeout */
	Mftimer.arg = NULL;					/* dummy value */
	set_timer (&Mftimer, atol (argv[1]) * 1000L);		/* set timer duration */
	start_detached_timer (&Mftimer);
	return 0;
}


static int
domfnow (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	Mftick (NULL);
	return 0;
}


static int
domfexclude (int argc, char *argv[], void *p OPTIONAL)
{
struct no_mf *nm;
int i;

	if (argc == 1) {	/*just list them*/
		for (nm = No_mf; nm != NULLMF; nm = nm->next)
			tprintf ("%s ", nm->area);
		tputc ('\n');
	} else {		/*add some call(s)*/
		for (i = 1; i < argc; i++) {
			if (strlen (argv[i]) > 8) {
				tprintf ("Invalid: %s\n", argv[i]);
				continue;
			}
			nm = callocw (1, sizeof (struct no_mf));

			strncpy (nm->area, argv[i], 20);
			/* add to list */
			nm->next = No_mf;
			No_mf = nm;
		}
	}
	return 0;
}


#ifdef HOLDMONITOR
static int domfhmode (int argc, char *argv[], void *p);
static int domfhclear (int argc, char *argv[], void *p);
static int domfhstatus (int argc, char *argv[], void *p);
#ifdef SOUNDS
static int domfhsound (int argc, char *argv[], void *p);
#endif

static struct cmds MFHOLDtab[] =
{
	{ "clear",	domfhclear,	0, 0, NULLCHAR },
	{ "mode",	domfhmode,	0, 0, NULLCHAR },
#ifdef SOUNDS
	{ "sound",	domfhsound,	0, 0, NULLCHAR },
#endif
	{ "status",	domfhstatus,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};


static int
domfhold (int argc, char *argv[], void *p)
{
	return subcmd (MFHOLDtab, argc, argv, p);
}


static int
domfhclear (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	HOLDindicator = 0;
#ifdef XSERVER
	xnotify (X_HOLD);
#endif
	return 0;
}


static int
domfhmode (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HOLDmode, "Monitoring for HELD messages", argc, argv);
}


static int
domfhstatus (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HOLDindicator, "HELD messages status", argc, argv);
}


#ifdef SOUNDS
static int
domfhsound (int argc, char *argv[], void *p OPTIONAL)
{
	return (setsoundstr (argc, argv, &HOLDsound));
}

#endif /* SOUNDS */

#endif /* HOLDMONITOR */


#ifdef ALERTMONITOR
static int domfaclear (int argc, char *argv[], void *p);
static int domfambox (int argc, char *argv[], void *p);
static int domfamode (int argc, char *argv[], void *p);
static int domfastatus (int argc, char *argv[], void *p);
#ifdef SOUNDS
static int domfasound (int argc, char *argv[], void *p);
#endif


static struct cmds MFALERTtab[] =
{
	{ "clear",	domfaclear,	0, 0, NULLCHAR },
	{ "mbox",	domfambox,	0, 0, NULLCHAR },
	{ "mode",	domfamode,	0, 0, NULLCHAR },
#ifdef SOUNDS
	{ "sound",	domfasound,	0, 0, NULLCHAR },
#endif
	{ "status",	domfastatus,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};


static int
domfalert (int argc, char *argv[], void *p)
{
	return subcmd (MFALERTtab, argc, argv, p);
}


static int
domfaclear (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	ALERTindicator = 0;
#ifdef XSERVER
	xnotify (X_ALERT);
#endif
	return 0;
}


static int
domfamode (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&ALERTmode, "Alerting of incoming messages to defined areas", argc, argv);
}


static int
domfastatus (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&ALERTindicator, "Incoming message alert", argc, argv);
}


static int
domfambox (int argc, char *argv[], void *p OPTIONAL)
{
struct mfalert *mf;
int i;

	if (argc == 1) {	/*just list them*/
		for (mf = mf_alerts; mf != NULLMFA; mf = mf->next)
			tprintf ("%s ", mf->area);
		tputc ('\n');
	} else {		/*add some area(s)*/
		for (i = 1; i < argc; i++) {
			if (strlen (argv[i]) > 8) {
				tprintf ("Invalid: %s\n", argv[i]);
				continue;
			}
			mf = callocw (1, sizeof (struct mfalert));

			strncpy (mf->area, argv[i], 20);
			/* add to list */
			mf->next = mf_alerts;
			mf_alerts = mf;
		}
	}
	return 0;
}


#ifdef SOUNDS
static int
domfasound (int argc, char *argv[], void *p OPTIONAL)
{
	return (setsoundstr (argc, argv, &ALERTsound));
}

#endif /* SOUNDS */


void
alertarea (char *area)
{
struct mfalert *mf;

	if (!ALERTmode)
		return;
	for (mf = mf_alerts; mf != NULLMFA; mf = mf->next) {
		if (!stricmp (area, mf->area)) {
			ALERTindicator = 1;
#ifdef XSERVER
			xnotify (X_ALERT);
#endif
#ifdef SOUNDS
			(void) playsound (ALERTsound);
#endif
		}
	}

}

#endif /* ALERTMONITOR */

#endif /* AX25 */
#endif /* MAILFOR */


/*************************************************************************/

int BBSlookup (char *bbs);

struct bbs MyFwds[NUMFWDBBS];
int Numfwds = 0;

#ifdef RLINE

/* Depending on the flag set, the mailbox will
 * read the message's original date,
 * the correct 'from' address (instead of the user%forwardbbs@myhost),
 * and for buls set the X-Forwarded options to prevent
 * unneccesary forward attempts
 * all from the R: lines supplied by the bbs system
 * 920311 - WG7J
 */
static int dobulrdate (int argc, char *argv[], void *p);
static int dorreturn (int argc, char *argv[], void *p);
static int dombloophold (int argc, char *argv[], void *p);
static int dofwdcheck (int argc, char *argv[], void *p);
static int dombhold (int argc, char *argv[], void *p);

int Rdate = 0;
int Rreturn = 0;
int Rfwdcheck = 1;

static struct cmds Rlinetab[] =
{
	{ "check",	dofwdcheck,	0, 0, NULLCHAR },
	{ "date",	dobulrdate,	0, 0, NULLCHAR },
	{ "hold",	dombhold,	0, 0, NULLCHAR },
	{ "loophold",	dombloophold,	0, 0, NULLCHAR },
	{ "return",	dorreturn,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};

void ReadFwdBbs (void);


static int
dobulrdate (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&Rdate, "Use R: for orig. date", argc, argv);
}


int Mbloophold = 2;

/* set loop detection threshold - WG7J */
static int
dombloophold (int argc, char *argv[], void *p OPTIONAL)
{
	return setint (&Mbloophold, "Loop hold after", argc, argv);
}


static int
dorreturn (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&Rreturn, "Use R: for ret. addr.", argc, argv);
}


static int
dofwdcheck (int argc, char *argv[], void *p OPTIONAL)
{
register int i;

	(void) setbool (&Rfwdcheck, "Use R: to check buls", argc, argv);
	if ((argc == 1) && Rfwdcheck && Numfwds) {	/*list the bbses we check*/
		tputs ("Checking for:");
		for (i = 0; i < Numfwds; i++)
			tprintf (" %s", MyFwds[i].name);
		tputc ('\n');
	} else {
		if (Rfwdcheck)
			ReadFwdBbs ();
	}
	return 0;
}


static int
dombhold (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&MbHolding, "Hold local bulletins for review", argc, argv);
}


int
dombrline (int argc, char **argv, void *p)
{
	return subcmd (Rlinetab, argc, argv, p);
}


#endif


#if defined(RLINE) || defined(MBFWD)
int
indexFwdBbs (char *name)
{
int k;

	for (k = 0; k < Numfwds; k++) {
		if (!stricmp (MyFwds[k].name, name))
			break;
	}
	if (k == Numfwds)
		k = NUMFWDBBS;
	return k;
}


void
ReadFwdBbs (void)
{
struct fwdbbs *f;

#if 0
	Numfwds = 0;		/* reset - doing this breaks the 'for laston' command */
#endif
	while (Numfwds < NUMFWDBBS) {
		f = fwdread (NULLCHAR, Numfwds);	/* get first BBS entry */
		if (f == NULLFWDBBS)
			break;
		if (indexFwdBbs (f->name) == NUMFWDBBS) {
			MyFwds[Numfwds].subchannel = f->subchannel;
			MyFwds[Numfwds].laston = (time_t) 0;
			strncpy (MyFwds[Numfwds++].name, strupr (f->name), FWDBBSLEN);
		}
#ifdef MBFWD
		fwdfree (&f);
#endif
	}

	return;
}

#endif /* RLINE */


int
BBSlookup (char *bbs)
{
int retval;

	for (retval = 0; retval < Numfwds; retval++)
		if (!stricmp (MyFwds[retval].name, bbs))
			break;
	if (retval == Numfwds)
		return -1;
	else
		return retval;
}
