/*****************************************************************************
 *
 * File ..................: mbfido/mbfido.c
 * Purpose ...............: Process Fidonet style mail and files.
 * Last modification date : 20-Nov-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.
 *****************************************************************************/

#include "../include/libs.h"
#include "../include/mbse.h"
#include "../include/structs.h"
#include "../include/records.h"
#include "../common/common.h"
#include "../dbase/dbcfg.h"
#include "../dbase/dbnode.h"
#include "../dbase/dbmsgs.h"
#include "../dbase/dbuser.h"
#include "../dbase/dbftn.h"
#include "../dbase/dbtic.h"
#include "../msgbase/msg.h"
#include "flock.h"
#include "putil.h"
#include "tosspkt.h"
#include "pack.h"
#include "ulock.h"
#include "tic.h"
#include "fsort.h"
#include "scan.h"
#include "mbfido.h"
#include "tracker.h"
#include "notify.h"
#include "rollover.h"
#include "hatch.h"
#include "maketags.h"


#define	UNPACK_FACTOR 300


int	do_toss    = FALSE;		/* Toss flag			    */
int	do_scan    = FALSE;		/* Scan flag			    */
int	do_tic     = FALSE;		/* Process .tic files		    */
int	do_notify  = FALSE;		/* Create notify messages	    */
int	do_roll	   = FALSE;		/* Rollover only		    */
int	do_full    = FALSE;		/* Full mailscan		    */
int	do_tags    = FALSE;		/* Create taglists		    */
int	do_test    = FALSE;		/* Test routing			    */
int	check_crc  = TRUE;		/* Check .tic crc values	    */
int	check_dupe = TRUE;		/* Check duplicates		    */
extern	int do_quiet;			/* Quiet flag			    */
extern	int e_pid;			/* Pid of child process		    */
extern	int show_log;			/* Show logging on screen	    */
int	do_unprot  = FALSE;		/* Unprotected inbound flag	    */
time_t	t_start;			/* Start time			    */
time_t	t_end;				/* End time			    */
int	packets    = 0;			/* Tossed packets		    */
int	packets_ok = 0;			/* Tossed packets Ok.		    */



extern	int net_in, net_imp, net_out, net_bad;
extern	int echo_in, echo_imp, echo_out, echo_bad, echo_dupe;
extern	int tic_in, tic_imp, tic_out, tic_bad, tic_dup;
extern	int Magics, Hatched;
extern	int notify, filemgr, areamgr;


/*
 * If we don't know what to type
 */
void Help(void)
{
	do_quiet = FALSE;
	ProgName();

	colour(11, 0);
	printf("\nUsage: mbfido [command(s)] <options>\n\n");
	colour(9, 0);
	printf("	Commands are:\n\n");
	colour(3, 0);
	printf("	n    notify <nodes>	Send notify messages\n");
	printf("	r    roll		Rollover statistic counters\n");
	printf("	s    scan		Scan outgoing Fido mail\n");
	printf("	ta   tag		Create taglists\n");
	printf("	te   test		Do some testing\n");
	printf("	ti   tic		Process .tic files\n");
	printf("	to   toss		Toss incoming Fido mail\n\n");
	colour(9, 0);
	printf("	Options are:\n\n");
	colour(3, 0);
	printf("	-f   -full		Full Mailscan\n");
	printf("	-noc -nocrc		Skip CRC checking\n");
	printf("	-nod -nodupe		Skip dupe checking\n");
	printf("	-q   -quiet		Quiet mode\n");
	printf("	-u   -unprotect		Toss unprotected inbound\n");
	colour(7, 0);
	printf("\n");
	die(0);
}



/*
 * Header, only if not quiet.
 */
void ProgName(void)
{
	if (do_quiet)
		return;

	colour(15, 0);
	printf("\nMBFIDO: MBSE BBS %s - Fidonet File and Mail processor\n", MBSEVersion);
	colour(14, 0);
	printf("        %s\n", Copyright);
}



void die(int onsig)
{
	/*
	 * First check if there is a child running, if so, kill it.
	 */
	if (e_pid) {
		if ((kill(e_pid, SIGTERM)) == 0)
			Syslog('+', "SIGTERM to pid %d succeeded", e_pid);
		else {
			if ((kill(e_pid, SIGKILL)) == 0)
				Syslog('+', "SIGKILL to pid %d succeeded", e_pid);
			else
				WriteError("Failed to kill pid %d", e_pid);
		}

		/*
		 * In case the child had the tty in raw mode, reset the tty.
		 */
		system("stty sane");
	}

	/*
	 * Check for locked and open message base.
	 */
	if (MsgBase.Locked)
		Msg_UnLock();
	if (MsgBase.Open)
		Msg_Close();

	signal(onsig, SIG_IGN);

	if (!do_quiet) {
		show_log = TRUE;
		colour(3, 0);
	}

	if (onsig) {
		if (onsig <= NSIG)
			WriteError("$Terminated on signal %d (%s)", onsig, SigName[onsig]);
		else
			WriteError("Terminated with error %d", onsig);
	}

	if (echo_imp + net_imp + net_out + echo_out)
		CreateSema((char *)"msglink");

	if (echo_out + net_out + tic_out)
		CreateSema((char *)"scanout");

	if (tic_imp)
		CreateSema((char *)"reqindex");

	if (net_in + net_imp + net_out + net_bad)
		Syslog('+', "Netmail  in [%4d] import [%4d] out [%4d] bad [%4d]", net_in, net_imp, net_out, net_bad);
	if (echo_in + echo_imp + echo_out + echo_bad + echo_dupe)
		Syslog('+', "Echomail in [%4d] import [%4d] out [%4d] bad [%4d] dupe [%4d]", echo_in, echo_imp, echo_out, echo_bad, echo_dupe);
	if (tic_in + tic_imp + tic_out + tic_bad + tic_dup + Hatched) {
		Syslog('+', "TICfiles in [%4d] import [%4d] out [%4d] bad [%4d] dupe [%4d]", tic_in, tic_imp, tic_out, tic_bad, tic_dup);
		Syslog('+', "     Magics [%4d]  hatch [%4d]", Magics, Hatched);
	}
	if (notify + areamgr + filemgr)
		Syslog('+', "Notify msgs [%4d] AreaMgr [%4d] FileMgr [%4d]", notify, areamgr, filemgr);

	time(&t_end);
	Syslog(' ', "MBFIDO finished in %s", t_elapsed(t_start, t_end));
	ulockunpack();

	if (!do_quiet)
		colour(7, 0);
	ExitClient(onsig);
}



int main(int argc, char **argv)
{
	int	i, Loop;
	char	*cmd, Options[81];
	struct	passwd *pw;
	struct	tm *t;

	InitConfig();
	memset(&Options, 0, sizeof(Options));

	/*
	 * Initialize global variables, data records.
	 */
	InitNode();
	InitMsgs();
	InitTic();
	InitUser();
	InitFidonet();
	TermInit(1);
	time(&t_start);
	t = localtime(&t_start);
	Diw = t->tm_wday;
	Miy = t->tm_mon;
	umask(002);

	/*
	 * Catch all the signals we can, and ignore the rest.
	 */
	for(i = 0; i < NSIG; i++) {

		if ((i == SIGINT) || (i == SIGBUS) || (i == SIGILL) ||
		    (i == SIGSEGV) || (i == SIGTERM) || (i == SIGKILL))
			signal(i, (void (*))die);
		else
			signal(i, SIG_IGN);
	}

	if (argc < 2)
		Help();

	cmd = xstrcpy((char *)"Command line: mbfido");

	for (i = 1; i < argc; i++) {
		cmd = xstrcat(cmd, (char *)" ");
		cmd = xstrcat(cmd, tl(argv[i]));

		if (strncmp(tl(argv[i]), "n", 1) == 0) {
			do_notify = TRUE;
			if (((i + 1) < argc) &&
                            ((strchr(argv[i + 1], ':') != NULL) || 
			     (atoi(argv[i + 1])) ||
			     (strncmp(argv[i + 1], "*", 1) == 0))) {
				sprintf(Options, "%s", argv[i + 1]);
				i++;
			}
		}
		if (strncmp(tl(argv[i]), "r", 1) == 0)
			do_roll = TRUE;
		if (strncmp(tl(argv[i]), "s", 1) == 0)
			do_scan = TRUE;
		if (strncmp(tl(argv[i]), "ta", 2) == 0)
			do_tags = TRUE;
		if (strncmp(tl(argv[i]), "ti", 2) == 0)
			do_tic = TRUE;
		if (strncmp(tl(argv[i]), "te", 2) == 0)
			do_test = TRUE;
		if (strncmp(tl(argv[i]), "to", 2) == 0) 
			do_toss = TRUE;
		if (strncmp(tl(argv[i]), "-f", 2) == 0)
			do_full = TRUE;
		if (strncmp(tl(argv[i]), "-noc", 4) == 0)
			check_crc = FALSE;
		if (strncmp(tl(argv[i]), "-nod", 4) == 0)
			check_dupe = FALSE;
		if (strncmp(tl(argv[i]), "-q", 2) == 0)
			do_quiet = TRUE;
		if (strncmp(tl(argv[i]), "-u", 2) == 0)
			do_unprot = TRUE;
	}

	ProgName();
	pw = getpwuid(getuid());
	InitClient(pw->pw_name, (char *)"mbfido", CFG.location, CFG.logfile, CFG.util_loglevel);

	Syslog(' ', " "); 
	Syslog(' ', "MBFIDO v%s", MBSEVersion);
	Syslog(' ', cmd);

	if (lockunpack())
		die(0);

	if (initnl())
		die(101);

	Rollover();

	if (!do_quiet)
		printf("\n");

	if (do_notify)
		if (Notify(Options))
			packmail();

	if (do_tic) {
		/*
		 *  Hatch new files and process .tic files
		 *  until nothing left to do.
		 */
		Loop = TRUE;
		do {
			Hatch();
			switch (Tic()) {
				case -1: die(0);
					 break;
				case 0:  Loop = FALSE;
					 break;
				default: break;
			}
		} while (Loop);
	}

	if (do_scan)
		ScanMail(do_full);

	if (do_toss)
		if (TossMail() == FALSE)
			die(0);

	if (do_test)
		TestTracker();

	if (do_tags)
		MakeTags();

	die(0);
	return 0;
}



/*
 * Toss Fidonet mail
 */
int TossMail(void)
{
	char	*inbound, *fname;
	DIR	*dp;
	struct	dirent *de;
	struct	stat sbuf;
	int	files = 0, files_ok = 0;
	int	rc = 0, maxrc = 0;
	fd_list	*fdl = NULL;

	if (do_unprot)
		inbound = xstrcpy(CFG.inbound);
	else
		inbound = xstrcpy(CFG.pinbound);

	Syslog('+', "Pass: toss netmail (%s)", inbound);

	if (chdir(inbound) == -1) {
		WriteError("$Can't chdir(%s)", inbound);
		die(0);
	}

	/*
	 * First toss any netmail packets.
	 */
	maxrc = rc = TossPkts();
	chdir(inbound);

	/*
	 * Scan the directory for ARCmail archives. The archive extension
	 * numbering doesn't matter, as long as there is something, so
	 * all kind of ARCmail naming schemes are recognized.
	 */
	if ((dp = opendir(inbound)) == NULL) {
		WriteError("$Can't opendir(%s)", inbound);
		die(0);
	}

	Syslog('+', "Pass: toss ARCmail (%s)", inbound);

	/*
	 * Add all ARCmail filenames to teh memory array.
	 */
	sync();
	while ((de=readdir(dp)))
		if ((strlen(de->d_name) == 12) &&
		    ((strncasecmp(de->d_name+8,".su",3) == 0) ||
		     (strncasecmp(de->d_name+8,".mo",3) == 0) ||
		     (strncasecmp(de->d_name+8,".tu",3) == 0) ||
		     (strncasecmp(de->d_name+8,".we",3) == 0) ||
		     (strncasecmp(de->d_name+8,".th",3) == 0) ||
		     (strncasecmp(de->d_name+8,".fr",3) == 0) ||
		     (strncasecmp(de->d_name+8,".sa",3) == 0))) {
			stat(de->d_name, &sbuf);
			fill_fdlist(&fdl, de->d_name, sbuf.st_mtime);
		}

	closedir(dp);
	sort_fdlist(&fdl);

	/*
	 * Now process the archives, the oldest first.
	 */
	while ((fname = pull_fdlist(&fdl)) != NULL) {
		files++;
		IsDoing("Unpack arcmail");

		if (checkspace(inbound, fname, UNPACK_FACTOR))
			if ((rc = unpack(fname)) == 0) {
				files_ok++;
				sync();
				rc = TossPkts();
				chdir(inbound);
			} else 
				WriteError("Error unpacking file %s", fname);
		else 
			Syslog('!', "Insufficient space to unpack file %s", fname);
		if (rc > maxrc) 
			maxrc = rc;
	}

	if ((files || packets) && 
            ((files_ok != files) || (packets_ok != packets)))
		Syslog('!', "Processed %d of %d files, %d of %d packets, rc=%d",
			files_ok, files, packets_ok, packets, maxrc);

	return TRUE;
}



/*
 *  Toss all packets currently in the inbound. Tossing is sorted by
 *  age of the files.
 */
int TossPkts(void)
{
	char	*inbound = NULL, *fname;
	DIR	*dp;
	struct	dirent *de;
	struct	stat sbuf;
	int	rc = 0, maxrc = 0;
	fd_list	*fdl = NULL;

	IsDoing("Tossing mail");

	if (do_unprot)
		inbound = xstrcpy(CFG.inbound);
	else
		inbound = xstrcpy(CFG.pinbound);

	if ((dp = opendir(inbound)) == NULL) {
		WriteError("$Can't opendir(%s)", inbound);
		return FALSE;
	}

	/*
	 * Read all .pkt filenames, get the timestamp and add them
	 * to the memory array for later sort on filedate.
	 */ 
	while((de = readdir(dp)))
	 	if ((strlen(de->d_name) == 12) &&
		    (strncasecmp(de->d_name+8,".pkt",4) == 0)) {
			stat(de->d_name, &sbuf);
			fill_fdlist(&fdl, de->d_name, sbuf.st_mtime);
		}

	closedir(dp);
	sort_fdlist(&fdl);

	/*
	 * Get the filenames, the oldest first until nothing left.
	 */
	while ((fname = pull_fdlist(&fdl)) != NULL) {
		packets++;

		/*
		 * See if "pktdate" from Tobias Ernst (or another
		 * preprocessor) is installed.
		 */
		if (strlen(CFG.pktdate)) {
			rc = execute(CFG.pktdate, fname, (char *)NULL, (char *)"/dev/null", (char *)"/dev/null", (char *)"/dev/null");
			Syslog('+', "%s preprocessing rc=%d", fname, rc);
		}

		if ((rc = toss(fname)) == 0) 
			packets_ok++;
		else 
			WriteError("Error tossing packet %s", fname);
		if (rc > maxrc) 
			maxrc = rc;
	}

	if (inbound)
		free(inbound);

	packmail();
	return maxrc;
}



/*
 * Toss one packet 
 */
int toss(char *fn)
{
	int	rc, ld;
	char	newname[16];

	rc = 0;
	/*
	 * Lock the packet
	 */
	if ((ld = f_lock(fn)) == -1) 
		return 1; 

	rc = TossPkt(fn);
	if (rc == 0) {
		unlink(fn); 
	} else {
		strncpy(newname,fn,sizeof(newname)-1);
		strcpy(newname+8,".bad");
		rename(fn,newname);
	}

	funlock(ld); 
	return rc;
}



