#include <ascii.h>
#include <fido.h>
#include <xfbuf.h>

long sizmem();
char *getmem();
char *next_arg();

/*

	An automatic mailer for Fido; given a text 
	file in the format below, generates FidoNet 
	messages automatically. 

	T. Jennings 8 Aug 86

*/

struct _msg msg;		/* message header structure */
struct _fido fido;		/* big system file */
struct _area msgarea;
struct _nmap nmap;
struct _ndat ndat;
struct _ntc ntc;

int nodefile;			/* node file handles */
int ntcfile;
int ndatfile;
int idxfile;
int ndxfile;
int zdxfile;

int nodetotal;			/* satisfy link */
int connect_tries,dial_tries;	/* satisfy link */

char nodepath[80] = "";		/* current directory */

struct _node id,altid;		/* our address */
struct _node dest;		/* work node */

int msgtotal;			/* total number of messages */
int msghighest;			/* highest number */
int log;			/* log file handle */

char *txbuff;			/* message text buffer, */
unsigned txsize;		/* buffer size, */
unsigned txcount;		/* current count */

char bymsg[SS];

int _stack = 5000;

main(argc,argv)
int argc;
char *argv[];
{
char arg[SS],path[SS],fname[SS];
struct _xfbuf xfbuf;
int n;

	printf("FidoMail (23 Jan 91) FidoNet Message Mailer for ");
	copr();

	if (argc < 2) {
		printf("No Message Script Files specified\r\n");
		exit(1);
	}

	allmem();				/* free memory, */
	fastfile(4);
	if (sizmem() > 65535L) txsize= 65535;
	else txsize= sizmem();
	txbuff= getmem(txsize);			/* get memory for input buffer */

	cpyarg(arg,argv[1]);			/* copy the filename */
	fixdir(arg);				/* in case directory name */

	log= append("fidonet.log");		/* add to the log file, */
	if (log == -1) {
		printf("ERROR: cant open FIDONET.LOG!\r\n");
		exit(1);
	}
	gtod(fname);
	lprintf("--------------\r\nFidoMail started %s\r\n",&fname[11]);

	n= open("fido.sys",0);				/* load system stuff */
	if (n == -1) {
		clprintf("The Fido system file \"FIDO.SYS\" is missing!\r\n");
		exit(1);
	}
	read(n,&fido,sizeof(fido));
	close(n);
	if (fido.fido_version != FIDOVER) {
		clprintf("Wrong Fido/FidoNet or FidoMail version!\r\n");
		exit(1);
	}
	id.zone= fido.zone;			/* node ID */
	id.net= fido.net;
	id.number= fido.number;
	id.point= fido.point;

	altid.zone= fido.altzone;		/* alternate ID */
	altid.net= fido.altnet;
	altid.number= fido.altnumber;
	altid.point= fido.altpoint;

	getarea(fido.netmarea,&msgarea);	/* select the msg area */

	if (open_node() == -1) {
		clprintf(" * Missing or wrong revision nodelist files; please run \"MAKELIST\"\r\n");

	} else {

		sprintf(bymsg," * from FidoMail v%d%c\r\n",FIDOVER,'a' + FILEVER - 1);
		msgcount();			/* count the messages, */
		strip_path(path,arg);		/* copy of the path only */
		xfbuf.s_attrib= 0;		/* normal attribute, */
		n= 0;
		while (_find(arg,n,&xfbuf)) {	/* process each file, */
			++n;
			cpyarg(fname,path);	/* build the full pathname, */
			strcat(fname,xfbuf.name);
			makemsg(fname);		/* process it, */
		}
		if (n == 0) {
			clprintf(" * No message script files to do\r\n");
		}
		gtod(fname);
	}
	lprintf("Complete at %s\r\n------------\r\n",&fname[11]);
	close_node();
	close(log);				/* done with the log */
	exit(0);
}
/* Generate messages from the message script file. Output reports as we go. */

makemsg(fname)
char *fname;
{
char *lp,ln[SS];	/* line buffer */
char arg[SS];		/* line command */
char nodes[SS];		/* list of nodes */

int f;			/* open file */
int recd;		/* nodelist record */
struct _msg msg;	/* message header */

int state;
#define FROM 1				/* got a From: line */
#define TO 2				/* got a To: line */
#define ON 4				/* got On: node list */
#define SUBJ 8				/* got Subj: or File: */
#define DONE (FROM + TO + ON + SUBJ)	/* means we are done */

	lp= (char *) &msg;
	for (f= 0; f < sizeof(struct _msg); f++) *lp++= NUL;

	f= open(fname,0);
	if (f == -1) {
		clprintf(" * No such file %s\r\n",fname);
		return;
	}

	*nodes= NUL;				/* no nodes yet, */
	msg.orig_node= id.number;		/* originating node */
	msg.orig_net= id.net;
/*	msg.orig_zone= id.zone;
*/	msg.up= 0;				/* clear reply links */
	msg.reply= 0;
	msg.times= 1;				/* mark as read once for TWIX */
	msg.attr= MSGLOCAL;			/* generated locally */
	msg.cost= 0;				/* cost so far */
	gtod(msg.date);				/* creation date */

	for (state= 0; state != DONE;) {

		if (! rline(f,ln,sizeof(ln))) {
			clprintf(" * File ended before the message was completed!\r\n");
			return;
		}

		lp= skip_delim(ln);	/* ignore leading garbage, */
		cpyatm(arg,lp);		/* a copy of the command word, */
		lp= next_arg(lp);	/* pointer to the following text */
		stolower(arg);

		if (same(arg,"from:")) {
			lp[sizeof(msg.from)]= NUL;
			strcpy(msg.from,lp);
			state |= FROM;

		} else if (same(arg,"to:")) {
			lp[sizeof(msg.to)]= NUL;
			strcpy(msg.to,lp);
			state |= TO;

		} else if (same(arg,"on:")) {
			strcpy(nodes,lp);
			state |= ON;

		} else if (same(arg,"subj:")) {
			lp[sizeof(msg.subj)]= NUL;
			strcpy(msg.subj,lp);
			state |= SUBJ;

		} else if (same(arg,"file:") || same(arg,"files:")) {
			lp[sizeof(msg.subj)]= NUL;
			strcpy(msg.subj,lp);
			state |= SUBJ;
			msg.attr |= MSGFILE;

		} else if (same(arg,"option:") || same(arg,"options:")) {
			while (num_args(lp)) {
				switch (*lp) {
					case 'p': case 'P': msg.attr |= MSGPRIVATE; break;
					case 'k': case 'K': msg.attr |= MSGKILL; break;
				}
				lp= next_arg(lp);
			}
		}
	}

/* We have all the basic args. Fill in the rest of the message structure,
send a message to each node specified. Watch that we delete any Control-Z
at the end of the file. */

	txbuff[read(f,txbuff,txsize)]= SUB;	/* load text, mark the end */
	for (txcount= 0; txbuff[txcount] != SUB; ++txcount);

	lp= nodes;				/* list of nodes */
	cpy_node(&dest,&id);			/* default to us */

	while (num_args(lp)) {
		cpyatm(arg,lp);			/* not cpyarg; / is a delimiter! */
		lp= next_arg(lp);		/* next item, */
		stolower(arg);

		if (same(arg,"ournet")) {	/* if meta "ournet" */
			clprintf(" * To OurNet:\r\n");
			cpy_node(&dest,&id);	/* choose us, */
			dest.number= 0;		/* make it the host, */
			recd= find_ndat(&dest);	/* find our host */
			if (recd == 0) {
				clprintf(" * Can't find our own host %s in the nodelist??\r\n",
				    str_node(&dest));

			} else {
				while (get_ndat(recd++)) {
					if (is_us(&ndat.node)) continue;
					if (! same_net(&ndat.node,&dest)) break;
					write_msg(&msg,f);
				}
			}
			clprintf("\r\n");

		} else {				/* hopefully normal net/node */
			cpy_node(&dest,&id);
			set_nn(arg,&dest);		/* change /number */
			if (find_ndat(&dest) != -1) {	/* write one message */
				write_msg(&msg,f);

			} else {
				clprintf(" * There is no %s\r\n",str_node(&dest));
			}
		}
	}
}

/* Write out the message as is, return 0 if an error. (Disk full).  The
global struct 'dest' is the actual destination. */

write_msg(m,fi)
struct _msg *m;
int fi;
{
int i,f;
char c,buff[SS],name[SF],*cp;

	m-> dest_node= dest.number;		/* set basic net/node address */
	m-> dest_net= dest.net;
/*	m-> dest_zone= dest.zone;
*/
	if (id.zone &&				/* do IFNA Kludge if */
	    !same_zone(&dest,&id) && 		/* not our zone */
	    !same_zone(&dest,&altid)) {
		m-> dest_node= dest.zone;	/* send to ourzone:1/destzone */
		m-> dest_net= 1;
	}
	m-> cost= ndat.cost;			/* set message cost, */
	clprintf(" * Message to %s\r\n",str_node(&dest));

	sprintf(name,"%d.MSG",msghighest + 1);
	makemname(buff,name);
	f= creat(buff,1);
	if (f == -1) {
		clprintf(" * Cant create message file %s\r\n",name);
		return;
	}
	write(f,m,sizeof(struct _msg));		/* write header (ignore error) */

	sprintf(buff,"\001INTL %s %s\r\n",str_node(&dest),str_node(&id));
	write(f,buff,strlen(buff));		/* IFNA Kludge */

	if (write(f,txbuff,txcount) != txcount) {
		clprintf(" * Disk Full!\r\n");	/* write text, check error */
		close(f);
		delete(name);
		return;
	}

/* Add the "via" line only of there is a message body -- allows generating
null "poll" messages. */

	for (i= 0; i < txcount; i++) {		/* count printable characters */
		if (txbuff[i] > ' ') {		/* (not including spaces) */
			write(f,bymsg,strlen(bymsg)); /* autodial's message */
			break;
		}
	}
	write(f,"",1);				/* write a null, */

	close(f);				/* message complete */
	++msghighest;				/* next highest message number, */
	++msgtotal;				/* another message */
}
/* Get message area #n; return 0 if not set, illegal number
or other error. */

getarea(n,a)
unsigned n;		/* area number */
struct _area *a;	/* struct to load */
{
int off,f,i;

	if (n < 0) return(0);				/* no such area */
	off= sizeof(struct _fido);			/* offset to beg msg areas */
	if (n >= fido.marea_max) return(0);		/* msg area max */

	f= open("fido.sys",0);
	if (f == -1) return(0);				/* no system file! */

	off += n * sizeof(struct _area);		/* offset plus area # */
	lseek(f,(0L + off),0);				/* seek there, */
	i= read(f,a,sizeof(struct _area));		/* read some, */
	close(f);					/* immediately close it */

	if (i != sizeof(struct _area)) return(0);	/* check read error */
	a-> number= n;					/* set the path number */
	return(*a-> path != NUL);			/* invalid if no path set */
}
/* Assemble a full msg filename */

makemname(d,p)
char *d,*p;
{
	strcpy(d,msgarea.path);		/* put in the path, */
	strcat(d,p);			/* add the filename */
}
/* Open a file for appending, creating it if necessary. Return the
open handle, or -1 if error. */

append(s)
char *s;
{
int h;

	h= open(s,2);				/* open or create the */
	if (h == -1) h= creat(s,2);		/* file, if opened OK */
	else lseek(h,0L,2);			/* seek to the end */
	return(h);				/* handle or error */
}

/* Count the number of messages and the highest message number, update the
system file array. */

msgcount() {
int n;
char buff[SS];
struct _xfbuf xfbuf;

	makemname(buff,"????????.msg");

	msgtotal= 0; msghighest= 0;
	xfbuf.s_attrib= 0;
	while (_find(buff,msgtotal,&xfbuf)) {
		++msgtotal;			/* count another, */
		n= atoi(xfbuf.name);		/* change name to number */
		if (n > msghighest) msghighest= n;/* pick highest one, */
	}
}
/* Formatted print to the log file. If the log fills up, mark the handle
as -1, stop writing to it. */

lprintf(f)
char *f;
{
char *cp,buf[500];

	if (! log) return;
	_spr(buf,&f);
	if (write(log,buf,strlen(buf)) != strlen(buf)) {
		printf("LOG FILE: DISK FULL!!\r\n");
		log= 0;
	}
}
/* Formatted print to the log file and console. */

clprintf(f)
char *f;
{
char buf[500];

	_spr(buf,&f);
	cputs(buf);

	if (! log) return;
	if (write(log,buf,strlen(buf)) != strlen(buf)) {
		printf("LOG FILE: DISK FULL!!\r\n");
		log= 0;
	}
}
/* Return true if the FBIT is set. */

fbit(bit)
int bit;
{
	return(fido.fbits[bit / sizeof(char)] & (1 << bit % sizeof(char)));
}
