#include <ascii.h>
#include "fido.h"
#include "fidomem.h"
#include <xfbuf.h>
#include "proto.h"

/* Add any new messages to existing packets; the IN TRANSIT (MSGFWD) bit
indicates which messages are in packets. This will perform the same task
as packet() below, but it's probably slower. */

void re_packet() {
int recd,m,n;
unsigned nodetotal;
int used,msg2;

	msg2= 1;
	for (nodetotal= 0; get_ntc(nodetotal) != -1; ) {
		++nodetotal;				/* count 'em */
	}

/* Now look for new messages that have not been packeted. */

	m= 0;						/* starting msg # - 1 */
	while (1) {
		if (evtabort()) return;			/* check manual abort */

		m= findmsg(++m,1);			/* open next message */
		if (! m) break;				/* (no more msgs) */

		if ((msg.attr & MSGFWD) ||		/* if packeted OR */
		    !send_this()) {			/* not this sched */
			closemsg();			/* close open msg */
			continue;
		}
		recd= out_xlate();			/* find dest node */
		if (recd > 0) {				/* if OK, */

/* Either locate an existing NTC record or create a new one. If one exists,
but the packet was sent, then the contents are not valid. (Flag this for
the end of sched report.) */

			n= find_ntc(&nmap.node);	/* search for it */
			used= (n != -1) && (ntc.bits & NTC_SUCCESS);
			n= load_ntc(recd - 1,used);	/* get/make NTC recd */
			if (n == -1) n= nodetotal++;	/* (new NTC recd) */
			if (msg2) cprintf(SM+220);	/* "adding to existing packets" */
			msg2= 0;
			closemsg();			/* close -- pkt_this opens */
			pkt_this(n,m,*msg_total());	/* add to packet */

		} else if (recd < 0) {			/* if orphan mark it */
			clprintf(SM+221,m,str_node(&msg_orig),str_node(&msg_dest));
			msg.attr |= MSGORPHAN;
			wrtmsg();			/* write & close */

		} else closemsg();
	}
}

/* Create all of the packets as directed by the current route table. 
NOTE: This assumes that set_abort() was used somewhere, as thats how we
exit if the disk is full. */

make_packets() {
int s,i,f,n;			/* junk */
char write;
char *cp,name[SS];
struct _xfbuf xfbuf;
int recd;
unsigned nodetotal;

/* Go through the message system, and create a .NTC record for each node
that has mail to it. The first pass through finds the first message number
and the number of messages (speeds up the second pass) and creates the .NTC
records. The second pass creates a packet to match each .NTC record. */

	nodetotal= 0;

/* Create a .NTC record for each node we are to POLL. */

	cprintf(SM+222);				/* Searching for nodes to Poll */
	for (recd= 1; (recd= find_poll(recd)) != -1; ++recd) {
		if (evtabort()) return;			/* check for manual abort */
		clr_ntc();				/* clear it out */
		cpy_node(&ntc.node,&nmap.node);		/* fill in node address */
		fix_node(&ntc.node);
		ntc.recd= recd;				/* .NMP, .SYS record number */
		if (nmap.bits & NMAP_HOLD) ntc.bits |= NTC_HOLD;
		if (nmap.bits & NMAP_PU) ntc.bits |= NTC_PU;
		put_ntc(nodetotal++);
	}

/* Pass 1: examine each message, check it's destination, locate or
create a .NTC record for each destination we find.

We also remember the first message number and the number of messages for
each destination; These are used to bound the message search for creating the
packets, greatly speeding up the process. */

	cprintf(SM+223);				/* "checking for outgoing messages" */
	s= 0; 
	makemname(name,"*.MSG");			/* setup for raw */
	xfbuf.s_attrib= 0; 				/* msg file search */
	while (_find(name,s++,&xfbuf)) {		/* take em as we find em */
		if (evtabort()) return;			/* check for manual abort */
		i= findmsg(atoi(xfbuf.name),0);		/* load the msg */
		if (! i) continue;			/* eh? */
		write= 0;				/* 0 == msg not modified */
		if (msg.attr & MSGFWD) write= 1;	/* IN-TRANSIT must be valid */
		msg.attr &= ~(MSGFWD | MSGORPHAN);	/* strip unwanted bits */
		if (send_this()) {			/* check it */
			recd= out_xlate();		/* find dest node */
			if (recd > 0) {			/* if OK, */
				n= load_ntc(recd - 1,0);/* get/make NTC recd */
				if (n == -1) n= nodetotal++; /* (new NTC recd) */
				++ntc.msgs;		/* count another, */
				if ((ntc.files == 0) || (i < ntc.files))
					ntc.files= i;	/* set message # */
				put_ntc(n);		/* save it, */

			} else if (recd < 0) {		/* if orphan mark it */
				clprintf(SM+221,i,str_node(&msg_orig),str_node(&msg_dest));
				msg.attr |= MSGORPHAN;
				write= 1;		/* write it out */
			}
		}
		if (write) wrtmsg();			/* write it out */
		else closemsg();			/* or just close it */
		++i;
	}

/* Now create a packet for each .NTC record, that is, for each node that
has one or more messages to it this schedule. This creates .PKT and .FLO
files for each node. There may be zero messages to packet; these are the
POLLed nodes. */

	for (recd= 0; get_ntc(recd) != -1; ++recd) {
		if (evtabort()) return;			/* manual abort */
		i= ntc.files; ntc.files= 0;		/* first message number (if any) */
		n= ntc.msgs; ntc.msgs= 0;		/* number of messages (if any) */
		pkt_this(recd,i,n);			/* packet for each node */
	}
}

/* Locate or create the .NTC record for the node specified by the current
nmap record; if new, clear it out. Return the NTC record number, or -1 if
we are starting a new record. */

static load_ntc(recd,clear)
int recd;	/* nodelist record number to link to */
int clear;	/* 1 == clear existing NTC recd */
{
int n;

	fix_node(&nmap.node);			/* make it logical */
	n= find_ntc(&nmap.node);
	if ((n == -1) || clear) {
		clr_ntc();
		cpy_node(&ntc.node,&nmap.node);
		fix_node(&ntc.node);
		ntc.recd= recd;
		if (nmap.bits & NMAP_HOLD) ntc.bits |= NTC_HOLD;
		if (nmap.bits & NMAP_PU) ntc.bits |= NTC_PU;
		if (ext_mail) nmap.bits |= NTC_SUCCESS;

		if (clear) ntc.bits |= NTC_KILLED; /* mark as re-used */
	}
	ntc.bits &= ~NTC_REPACKET;		/* always clear this */
	return(n);
}

/* Generate the packet and file list for the node specified by this .NTC
record, starting at the specified message number and continuing for
the specified number of messages. (That is for speed purposes only.) */

static pkt_this(recd,msgno,number)
int recd;		/* .NTC record number */
int msgno;		/* first message number */
int number;		/* number of messages */
{
int s,i,n;			/* junk */
char *cp,name[SS],buff[SS];
struct _xfbuf xfbuf;
int file,flo;			/* packet file, file list file, */

	clprintf(SM+216,str_node(&ntc.node),(ntc.bits & NTC_HOLD ? string(SM+153) : ""));

	sprintf(buff,0,"%d.OUT",ntc.recd);	/* outgoing packet */
	makeuname(name,buff);
	file= open(name,2);			/* try to open first, */
	if (file == -1) {			/* if it doesnt exist */
		file= creat(name,2);		/* create it */
		if (file == -1) {
			clprintf(SM+10,name);
			frc_abort();
		}
		make_header(file,&ntc.node);	/* create new empty packet */

	} else {
		xfbuf.s_attrib= 0;		/* position to after */
		_find(name,0,&xfbuf);		/* the last msg */
		xfbuf.fsize -= 2L;		/* (any 'long' sitting around...) */
		lseek(file,xfbuf.fsize,0);	/* (write over end-packet) */
	}

/* Create the file list for this packet. */

	sprintf(buff,0,"%d.FLO",ntc.recd);	/* outgoing packet */
	makeuname(name,buff);
	flo= open(name,1);			/* reopen existing */
	if (flo == -1) flo= creat(name,1);	/* or make new one */
	if (flo == -1) {
		clprintf(SM+10,name);		/* "can't create" */
		close(file);
		frc_abort();
		return;
	}

/* Find all the messages to go into this packet. Translate the destination
node number into the real one, and install that into the message header. */

	while (msgno= findmsg(msgno,1)) {
		i= out_xlate();			/* yo, where this msg go? */

		if (send_this() && 		/* IF sendable AND */
		    (!(msg.attr & MSGFWD)) &&	/*  not packeted AND */
		    (i > 0) &&			/*  found node AND */
		    same_node(&ntc.node,&nmap.node)) { /* right node THEN */
			++ntc.msgs;		/* count another sent */
			clprintf(SM+226,msgno,str_node(&msg_orig),str_node(&msg_dest));

/* Here we honor file attaches and requests. For attaches we simply put all of
the filenames into the .FLO list; for requests we create the .REQ file of
the right name and put all the names (CR/LF delimited) into it, and then
file attach the .REQ file. (addmsg takes care of stripping pathnames off the
message itself.) */

			if (msg.attr & MSGFREQ) {
				sprintf(buff,0,"%04x%04x.REQ",msg.dest_net,msg.dest_node);
				makeuname(name,buff);	/* in outbound dir */
				i= open(name,2);	/* open .REQ file */
				if (i != -1) lseek(i,0L,2); /* append to existing */
				else i= creat(name,1);	/* or create anew */
				if (i == -1) {
					clprintf(SM+10,name); /* "can't create" */
					closemsg();
					frc_abort();
				}
				for (cp= skip_delim(msg.subj); *cp; ) {
					if (delim(*cp)) {
						write(i,"\r\n",2);
						cp= skip_delim(cp);

					} else write(i,cp++,1);
				}
				write(i,"\r\n",2);
				close(i);

				if (write(flo,name,strlen(name)) != strlen(name)) frc_abort();
				write(flo," ",1);
				++ntc.files;
			}
			if (msg.attr & MSGFILE) {
				clprintf(SM+62,msg.subj); /* "files" */
				if (write(flo,msg.subj,strlen(msg.subj)) != strlen(msg.subj)) frc_abort();
				write(flo," ",1);
				++ntc.files;
			}
			addmsg(file);		/* add to the packet */
			msg.attr |= MSGFWD;	/* mark "IN TRANSIT" */
			wrtmsg();		/* write it out (close it) */
			--number;		/* count another packeted */
		}
		closemsg();			/* (may already be closed) */
		if (! number) break;		/* stop if no more to do */
		++msgno;			/* next msg number */
	}
	end_hdr(file);
	close(file);
	close(flo);
	put_ntc(recd);				/* remember the counts */
	if (!ntc.msgs) clprintf(SM+68);		/* "none" */
}
/* Clear out the NTC record. */

static clr_ntc() {
char *cp;
int i;

	cp= (char *) &ntc; i= sizeof(struct _ntc);
	while (i--) *cp++= NUL;
}

/* Return true if this message should be sent. */

static send_this() {

	if (msg.attr & MSGSENT) return(0);	/* already sent: NO */
	if (! is_us(&msg_dest)) return(1);	/* not To: our node: YES */

	return(0);				/* (to us) */
}

/* Create the packet header, and write it to disk. Abort if the disk is full. */

static make_header(file,n)
int file;
struct _node *n;
{
int i;
struct _pkthdr pkthdr;
char *cp;

	cp= (char *) &pkthdr;			/* clear it all */
	for (i= 0; i < sizeof(pkthdr); ++i) *cp++= NUL;

	pkthdr.orig_number= id.number;	/* assemble it all, */
	pkthdr.orig_net= id.net;
	pkthdr.orig_zone= id.zone;

	pkthdr.dest_number= n-> number;		/* destination node */
	pkthdr.dest_net= n-> net;
	pkthdr.dest_zone= n-> zone;

	pkthdr.year= gtod3(0);			/* set date, etc */
	pkthdr.month= gtod3(1);
	pkthdr.day= gtod3(2);
	pkthdr.hour= gtod3(4);
	pkthdr.minute= gtod3(5);
	pkthdr.second= gtod3(6);

	pkthdr.ver= PKTVER; 			/* packet version */

	sprintf(pkthdr.extra,0,"FidoNet %d%c",FIDOVER,96+FILEVER);

	if (write(file,&pkthdr,sizeof(pkthdr)) != sizeof(pkthdr))
		frc_abort();
}

/* Add the current message to the packet. Abort if disk full. Add a line
to the end saying how it was routed. */

static addmsg(file)
int file;
{
int i,n;
char c,*p,*s,path[SS],arg[SS];
char buf[513];				/* NOTE 512 + 1 !!! disk read buffer */

	cputi(file,2);			/* type= message, */
	cputi(file,msg.orig_node);	/* added v1 */
	cputi(file,msg.dest_node);	/* added v1 */
	cputi(file,msg.orig_net);	/* added v2 */
	cputi(file,msg.dest_net);	/* added v2 */

	cputi(file,msg.attr & MSGMASK);	/* clear local bits */
					/* write attribute, */
	cputi(file,msg.cost);		/* message cost, */
	cputstr(file,msg.date);		/* install date string, */
	cputstr(file,msg.to);
	cputstr(file,msg.from);
	if (msg.attr & MSGFILE) {	/* if file names, */
		p= msg.subj;
		buf[0]= NUL;		/* strip off all the path names */
		while (num_args(p)) {	/* put in only the file names */
			cpyarg(arg,p);
			s= strip_path(path,arg);
			strcat(buf,s);
			strcat(buf," ");
			p= next_arg(p);
		}
		cputstr(file,buf);

	} else cputstr(file,msg.subj);

/* Copy all the message text into the packet. */

	while (n= read(msgfile,buf,sizeof(buf) - 1)) {
		buf[n]= NUL;				/* terminate it */
		for (p= buf; *p; ++p);			/* find 1st null */
		i= p - buf; if (i == 0) break;		/* (1st char is null!) */
		if (write(file,buf,i) != i)		/* write it */
			frc_abort();
	}
	if (! is_us(&msg_orig)) {
		gtod(arg);
		sprintf(buf,0," * Via Fido %s %s (v%d%c)\r\n",str_node(&id),arg,FIDOVER,96+FILEVER);
		n= strlen(buf);
		if (write(file,buf,n) != n) frc_abort();
	}
	cputc(file,NUL);
}
/* Mark the end of the packet. */

static end_hdr(file)
int file;
{
	cputi(file,0);
}
/* Put a string into the packet file. Note that this also writes the
null terminator. */

static cputstr(file,s)
int file;
char *s;
{
int n;
	n= strlen(s) + 1;			/* plus null */
	if (write(file,s,n) != n) frc_abort();
}

/* Write a character to the file. */

static cputc(file,c)
int file;
char c;
{
	if (write(file,&c,1) != 1) frc_abort();
}

/* Write an integer to the packet. */

static cputi(file,i)
int file,i;
{
	if (write(file,&i,sizeof(i)) != sizeof(i))
		frc_abort();
}
