/*****************************************************************************
 *
 * File ..................: mbfido/scan.h
 * Purpose ...............: Scan for outgoing mail.
 * Last modification date : 31-Oct-1999
 *
 *****************************************************************************
 * Copyright (C) 1997-1999
 *   
 * Michiel Broek		FIDO:		2:2801/16
 * Beekmansbos 10		Internet:	mbroek@ux123.pttnwb.nl
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * MBSE BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#define	DB_MSGAREA

#include "../include/libs.h"
#include "../include/structs.h"
#include "../include/records.h"
#include "../common/common.h"
#include "../msgbase/msg.h"
#include "../msgbase/msgtext.h"
#include "../dbase/dbnode.h"
#include "addpkt.h"
#include "putil.h"
#include "pack.h"
#include "tracker.h"
#include "scan.h"


extern	int	do_quiet;
extern	int	net_out;
extern	int	net_bad;
extern	int	echo_out;
int		scanned;

#define	MAXSEEN 70


/*
 * Internal prototypes
 */
void ScanFull(void);
void ScanOne(char *, unsigned long);
void ExportEcho(sysconnect, unsigned long, fa_list **);
void ExportNet(unsigned long);



/*
 *  Scan for outgoing mail. If the file $MBSE_ROOT/tmp/scan.mail doesn't exist
 *  or not all messages are successfully scanned, or fullscan is ordered, the
 *  complete messagebase will be scanned. The csan.mail file is simple, it
 *  contains pairs of unsigned long values of areanumber and messagenumber.
 */
void ScanMail(int DoAll)
{
	int		DoFull = FALSE, i = 0;
	char		*Fname = NULL;
	FILE		*fp;
	posted		pm;

	if (DoAll) {
		DoFull = TRUE;
	} else {
		scanned = 0;
		Fname = calloc(128, sizeof(char));
		sprintf(Fname, "%s/tmp/scan.mail", getenv("MBSE_ROOT"));

		if ((fp = fopen(Fname, "r")) == NULL) {
			Syslog('+', "%s not found, forcing full mail scan", Fname);
			DoFull = TRUE;
		} else {
			while (fread(&pm, sizeof(posted), 1, fp) == 1) {
				Syslog('+', "Export message %lu from %s", pm.msg, pm.path);
				ScanOne(pm.path, pm.msg);
				i++;
			}
			fclose(fp);

			unlink(Fname); 

			if ((i != scanned) || (i == 0)) {
				Syslog('+', "Not all messages exported, forcing full mail scan");
				DoFull = TRUE;
			}
		} 
		free(Fname);
	}

	if (DoFull)
		ScanFull();

	if (echo_out || net_out)
		packmail();
	RemoveSema((char *)"mailout");
}



void ScanFull()
{
	char		sAreas[81];
	FILE		*pAreas;
	long		arearec = 0, sysstart, nextstart;
	unsigned long	Total, Number;
	int		i;
	sysconnect	Link;
	fa_list		*sbl = NULL;

	Syslog('+', "Full mailscan");
	IsDoing("Scanning mail");

	if (!do_quiet) {
		colour(9, 0);
		printf("Scanning mail\n");
		colour(3, 0);
		fflush(stdout);
	}

	sprintf(sAreas, "%s/etc/mareas.data", getenv("MBSE_ROOT"));
	if ((pAreas = fopen(sAreas, "r")) == NULL) {
		WriteError("Can't open %s", sAreas);
		return;
	}
	fread(&msgshdr, sizeof(msgshdr), 1, pAreas);

	while (fread(&msgs, msgshdr.recsize, 1, pAreas) == 1) {
		sysstart = ftell(pAreas);
		fseek(pAreas, msgshdr.syssize, SEEK_CUR);
		nextstart = ftell(pAreas);
		arearec++;

		if ((msgs.Active) && (msgs.Type == ECHOMAIL || msgs.Type == NETMAIL)) {
			if (!do_quiet) {
				colour(3, 0);
				printf("\r%5ld .. %-40s", arearec, msgs.Name);
				colour(13, 0);
				fflush(stdout);
			}

			if (Msg_Open(msgs.Base)) {
				if ((Total = Msg_Number()) != 0L) {
					Number = Msg_Lowest();

					do {
						if (CFG.slow_util && do_quiet && ((Number % 2) == 0))
							usleep(1);

						if (((Number % 10) == 0) && (!do_quiet)) {
							printf("%6lu\b\b\b\b\b\b", Number);
							fflush(stdout);
						}

						Msg_ReadHeader(Number);
						if (Msg.Local) {
							if (Msg_Lock(15L)) {
								Syslog('m', "Export %lu from area %ld", Number, arearec);

								/*
								 * Setup SEEN-BY lines
								 */
								if (msgs.Type == ECHOMAIL) {
									fill_list(&sbl, aka2str(msgs.Aka), NULL);
									fseek(pAreas, sysstart, SEEK_SET);
									for (i = 0; i < (msgshdr.syssize / sizeof(sysconnect)); i++) {
										fread(&Link, sizeof(sysconnect), 1, pAreas);
										if ((Link.aka.zone) && (Link.sendto) && (!Link.pause)) {
											fill_list(&sbl, aka2str(Link.aka), NULL);
										}
									}
									sort_list(&sbl);

									fseek(pAreas, sysstart, SEEK_SET);
									for (i = 0; i < (msgshdr.syssize / sizeof(sysconnect)); i++) {
										fread(&Link, sizeof(sysconnect), 1, pAreas);
										if (Link.aka.zone)
											ExportEcho(Link, Number, &sbl);
									}

									tidy_falist(&sbl);
								}
								if (msgs.Type == NETMAIL)
									ExportNet(Number);

								Msg.Local = FALSE;
								Msg.Arrived = time(NULL);
								Msg_WriteHeader(Number);
								Msg_UnLock();
							}
						}

					} while (Msg_Next(&Number) == TRUE);
				}

				Msg_Close();

				if (!do_quiet) {
					printf("      \b\b\b\b\b\b");
					fflush(stdout);
				}
			}

			/*
			 * Make sure to start at the next area.
			 */
			fseek(pAreas, nextstart, SEEK_SET);
		}
	}

	fclose(pAreas);

	if (!do_quiet) {
		printf("\r                                                        \r");
		fflush(stdout);
	}
}



void ScanOne(char *path, unsigned long MsgNum)
{
	char		sAreas[81];
	FILE		*pAreas;
	long		sysstart;
	unsigned long	Total, Area = 0;
	int		i;
	sysconnect	Link;
	fa_list		*sbl = NULL;

	IsDoing("Scanning mail");

	if (!do_quiet) {
		colour(9, 0);
		printf("Scanning mail\n");
		colour(3, 0);
		fflush(stdout);
	}

	sprintf(sAreas, "%s/etc/mareas.data", getenv("MBSE_ROOT"));
	if ((pAreas = fopen(sAreas, "r")) == NULL) {
		WriteError("Can't open %s", sAreas);
		return;
	}
	fread(&msgshdr, sizeof(msgshdr), 1, pAreas);

	/*
	 * Seek the path we want
	 */
	while (TRUE) {
		if (fread(&msgs, msgshdr.recsize, 1, pAreas) != 1) {
			fclose(pAreas);
			return;
		}
		Area++;
		sysstart = ftell(pAreas);
		fseek(pAreas, msgshdr.syssize, SEEK_CUR);
		if (strcmp(msgs.Base, path) == 0)
			break;
	}

	if ((msgs.Active) && (msgs.Type == ECHOMAIL || msgs.Type == NETMAIL)) {
		if (!do_quiet) {
			colour(3, 0);
			printf("\r%5ld .. %-40s", Area, msgs.Name);
			colour(13, 0);
			fflush(stdout);
		}

		if (Msg_Open(msgs.Base)) {
			if ((Total = Msg_Number()) != 0L) {
				if (Msg_ReadHeader(MsgNum)) {
					if (Msg.Local) {
						if (Msg_Lock(15L)) {
							scanned++;
							/*
							 * Setup SEEN-BY lines
							 */
							if (msgs.Type == ECHOMAIL) {
								fill_list(&sbl, aka2str(msgs.Aka), NULL);
								fseek(pAreas, sysstart, SEEK_SET);
								for (i = 0; i < (msgshdr.syssize / sizeof(sysconnect)); i++) {
									fread(&Link, sizeof(sysconnect), 1, pAreas);
									if ((Link.aka.zone) && (Link.sendto) && (!Link.pause)) {
										fill_list(&sbl, aka2str(Link.aka), NULL);
									}
								}
								sort_list(&sbl);

								fseek(pAreas, sysstart, SEEK_SET);
								for (i = 0; i < (msgshdr.syssize / sizeof(sysconnect)); i++) {
									fread(&Link, sizeof(sysconnect), 1, pAreas);
									if (Link.aka.zone) {
										ExportEcho(Link, MsgNum, &sbl);
									}
								}

								tidy_falist(&sbl);
							}
							if (msgs.Type == NETMAIL) {
								ExportNet(MsgNum);
							}

							Msg.Local = FALSE;
							Msg.Arrived = time(NULL);
							Msg_WriteHeader(MsgNum);
							Msg_UnLock();
						}
					}

				}
			}

			Msg_Close();
		}
	}

	fclose(pAreas);

	if (!do_quiet) {
		printf("\r                                                        \r");
		fflush(stdout);
	}
}



/*
 *  Export message to downlink. The messagebase is locked.
 */
void ExportEcho(sysconnect L, unsigned long MsgNum, fa_list **sbl)
{
	char	*p;
	int	seenlen, oldnet, flags = 0;
	char	sbe[16];
	fa_list	*tmpl;
	FILE	*qp;

	if ((!L.sendto) || L.pause || L.cutoff)
		return;

//	Syslog('m', "Export to %s", aka2str(L.aka));

	if ((qp = OpenPkt(msgs.Aka, L.aka, (char *)"qqq")) == NULL)
		return;

	flags |= (Msg.Private)		? M_PVT : 0;
	AddMsgHdr(qp, fido2faddr(msgs.Aka), fido2faddr(L.aka), flags, 0, Msg.Written, Msg.To, Msg.From, Msg.Subject);
	fprintf(qp, "AREA:%s\r", msgs.Tag);

	if (Msg_Read(MsgNum, 78)) {
		if ((p = (char *)MsgText_First()) != NULL) {
			do {
				fprintf(qp, "%s", p);
				if (strncmp(p, " * Origin:", 10) == 0)
					break;

				/*
				 * Only append CR if not the last line
				 */
				fprintf(qp, "\r");

				/*
				 * Append ^aTID line
				 */
				if (strncmp(p, "\001PID", 4) == 0)
					fprintf(qp, "\001TID: MBSE BBS %s\r", PIDVersion);

			} while ((p = (char *)MsgText_Next()) != NULL);
		}
	}

	seenlen = MAXSEEN + 1;
	/*
	 * Ensure that it will not match the first entry.
	 */
	oldnet = (*sbl)->addr->net - 1;
	for (tmpl = *sbl; tmpl; tmpl = tmpl->next) {
		if (tmpl->addr->net == oldnet)
			sprintf(sbe, " %u", tmpl->addr->node);
		else
			sprintf(sbe, " %u/%u", tmpl->addr->net, tmpl->addr->node);
		oldnet = tmpl->addr->net;
		seenlen += strlen(sbe);
		if (seenlen > MAXSEEN) {
			seenlen = 0;
			fprintf(qp, "\rSEEN-BY:");
			sprintf(sbe, " %u/%u", tmpl->addr->net, tmpl->addr->node);
			seenlen = strlen(sbe);
		}
		fprintf(qp, "%s", sbe);
	}
	fprintf(qp, "\r\001PATH: %u/%u\r", msgs.Aka.net, msgs.Aka.node);
	putc(0, qp);
	fclose(qp);

	echo_out++;
}



/*
 *  Export Netmail message, the messagebase is locked.
 */
void ExportNet(unsigned long MsgNum)
{
	char		*p, ext[4];
	int		rc, flags = 0;
	FILE		*qp;
	fidoaddr	Dest, Route;
	time_t		now;
	struct tm	*tm;
	char		flavor;

	Syslog('m', "Export netmail to %s (%s)", Msg.ToAddress, (Msg.Crash || Msg.Direct || Msg.FileAttach) ? "Direct" : "Routed");
	memcpy(&Dest, faddr2fido(parsefnode(Msg.ToAddress)), sizeof(fidoaddr));
	Dest.domain[0] = '\0';

	if (!(Msg.Crash || Msg.Direct || Msg.FileAttach)) {
		if (!TrackMail(Dest, &Route)) {
			Syslog('!', "No route to %s, message orphaned", Msg.ToAddress);
			Msg.Orphan = TRUE;
			net_bad++;
			return;
		}
	}

	Msg.Sent = TRUE;
	if (Msg.KillSent)
		Msg.Deleted = TRUE;

	if (Msg.Crash || Msg.Direct || Msg.FileAttach) {
		memset(&ext, 0, sizeof(ext));
		if (Msg.Crash)
			sprintf(ext, (char *)"ccc");
		else
			sprintf(ext, (char *)"nnn");

		if ((qp = OpenPkt(msgs.Aka, Dest, (char *)ext)) == NULL) {
			net_bad++;
			return;
		}

	} else {
		Syslog('m', "Route via %s", aka2str(Route));
		if (!SearchNode(Route, TRUE)) {
			WriteError("Routing node %s not in setup, aborting", aka2str(Route));
			return;
		}

		/*
		 *  Note that even if the exported netmail is not crash, that if
		 *  the routing node has crash status, this netmail will be send
		 *  crash.
		 */
		memset(&ext, 0, sizeof(ext));
		if (nodes.PackNetmail)
			sprintf(ext, (char *)"qqq");
		else if (nodes.Crash)
			sprintf(ext, (char *)"ccc");
		else if (nodes.Hold)
			sprintf(ext, (char *)"hhh");
		else
			sprintf(ext, (char *)"nnn");

		if ((qp = OpenPkt(msgs.Aka, Route, (char *)ext)) == NULL) {
			net_bad++;
			return;
		}
	}

	flags |= (Msg.Private)		? M_PVT   : 0;
	flags |= (Msg.Crash)		? M_CRASH : 0;
	flags |= (Msg.Hold)		? M_HOLD  : 0;
	flags |= (Msg.FileRequest)	? M_REQ   : 0;
	flags |= (Msg.FileAttach)	? M_FILE  : 0;
	flags |= (Msg.ReceiptRequest)	? M_RRQ   : 0;
	flags |= (Msg.ConfirmRequest)	? M_AUDIT : 0;

	AddMsgHdr(qp, fido2faddr(msgs.Aka), fido2faddr(Dest), flags, 0, Msg.Written, Msg.To, Msg.From, Msg.Subject);

	if (Msg_Read(MsgNum, 78)) {
		if ((p = (char *)MsgText_First()) != NULL) {
			do {
				fprintf(qp, "%s\r", p);
				if (strncmp(p, "--- MBSE", 8) == 0)
					break;
			} while ((p = (char *)MsgText_Next()) != NULL);
		}
	}

	now = time(NULL);
	tm = gmtime(&now);
	fprintf(qp, "\001Via %s @%d%02d%02d.%02d%02d%02d.01.UTC MBSE BBS %s\r",
		aka2str(msgs.Aka),
		tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
		tm->tm_hour, tm->tm_min, tm->tm_sec,
		MBSEVersion);

	putc(0, qp);
	fclose(qp);

	if (Msg.FileAttach) {
		if (Msg.Crash)
			flavor = 'c';
		else
			flavor = 'f';

		if (strlen(CFG.dospath)) {
			rc = attach(*parsefnode(Msg.ToAddress), Dos2Unix(Msg.Subject), LEAVE, flavor);
			Syslog('+', "FileAttach %s %s", Dos2Unix(Msg.Subject), rc ? "Success":"Failed");
		} else {
			rc = attach(*parsefnode(Msg.ToAddress), Msg.Subject, LEAVE, flavor);
			Syslog('+', "FileAttach %s %s", Msg.Subject, rc ? "Success":"Failed");
		}
	}

	net_out++;
}


