/*****************************************************************************
 *
 * File ..................: mbmsg/mbmsg.c
 * Purpose ...............: Message Base Maintenance
 * Last modification date : 21-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.
 *****************************************************************************/

#include "../include/libs.h"
#include "../include/mbse.h"
#include "../include/structs.h"
#include "../common/common.h"
#include "../msgbase/msg.h"
#include "../dbase/dbcfg.h"
#include "mbmsg.h"



int	do_pack = FALSE;	/* Pack flag				    */
int	do_kill = FALSE;	/* Kill flag (age and maxmsgs)		    */
int	do_index = FALSE;	/* Index flag				    */
int	do_link = FALSE;	/* Link messages			    */
extern	int do_quiet;		/* Quiet flag				    */
extern	int show_log;		/* Show loglines			    */
long	do_area = 0;		/* Do only one area			    */
time_t	t_start;		/* Start time				    */
time_t	t_end;			/* End time				    */
int	are_tot = 0;		/* Total areas				    */
int	are_proc = 0;		/* Areas processed			    */
int	msg_tot = 0;		/* Total messages			    */
int	msg_del = 0;		/* Deleted messages			    */
int	msg_link = 0;		/* Linked messages			    */
int	processed = FALSE;	/* Did process something		    */
int	oldmask;




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

	colour(15, 0);
	printf("\nMBMSG: MBSE BBS %s - Message Base Maintenance Utility\n", MBSEVersion);
	colour(14, 0);
	printf("       %s\n", Copyright);
	colour(7, 0);
}



int main(int argc, char **argv)
{
	int	i;
	char	*cmd;
	struct	passwd *pw;

	InitConfig();
	TermInit(1);
	oldmask = umask(0);
	time(&t_start);

	/*
	 * Catch all signals we can, and ignore or catch them
	 */
	for (i = 0; i < NSIG; i++) {
		if ((i == SIGHUP) || (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:");

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

		if (strncmp(argv[i], "i", 1) == 0)
			do_index = TRUE;
		if (strncmp(argv[i], "l", 1) == 0)
			do_link = TRUE;
		if (strncmp(argv[i], "k", 1) == 0)
			do_kill = TRUE;
		if (strncmp(argv[i], "p", 1) == 0)
			do_pack = TRUE;
		if (strncmp(argv[i], "-a", 2) == 0)
			do_area = atoi(&argv[i][5]);
		if (strncmp(argv[i], "-q", 2) == 0)
			do_quiet = TRUE;
	}

	if (!(do_index || do_link || do_kill || do_pack))
		Help();

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

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

	if (!do_quiet) {
		printf("\n");
		colour(3, 0);
	}

	memset(&MsgBase, 0, sizeof(MsgBase));

	DoMsgBase();

	die(0);
	return 0;
}



void Help()
{
	do_quiet = FALSE;
	ProgName();

	colour(12, 0);
	printf("\n	Usage: mbmsg [command(s)] <options>\n\n");
	colour(9, 0);
	printf("	Commands are:\n\n");
	colour(3, 0);
//	printf("	i  index	Create new index files\n");
	printf("	l  link		Link messages by subject\n");
	printf("	k  kill		Kill messages (age & count)\n");
	printf("	p  pack		Pack deleted messages\n");
	printf("\n");
	colour(9, 0);
	printf("	Options are:\n\n");
	colour(3, 0);
	printf("	-a -area<key>	Process area <key> only\n");
	printf("	-q -quiet	Quiet mode\n");

	printf("\n");
	die(0);
}



void die(int onsig)
{
	signal(onsig, SIG_IGN);
	if (!do_quiet) {
		printf("\r");
		colour(3, 0);
	}

	if (MsgBase.Locked)
		Msg_UnLock();
	if (MsgBase.Open)
		Msg_Close();

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

	if (onsig == SIGSEGV)
		Syslog('+', "Last msg area %s", msgs.Name);

	if (are_tot || are_proc || msg_link)
		Syslog('+', "Areas  [%5d] Processed [%5d] Linked [%5d]", are_tot, are_proc, msg_link);
	if (msg_tot || msg_del)
		Syslog('+', "Msgs   [%5d]   Deleted [%5d]", msg_tot, msg_del);

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

	umask(oldmask);
	if (!do_quiet) {
		colour(7, 0);
		printf("\r                                                          \n");
	}
	ExitClient(onsig);
}



void DoMsgBase()
{
	FILE	*pAreas;
	char	*sAreas;
	long	arearec;
	int	Del = 0;

	sAreas     = calloc(81, sizeof(char));

	IsDoing("Msg Maintenance");

	if (do_area)
		Syslog('+', "Processing message area %ld", do_area);
	else
		Syslog('+', "Processing all message areas");

	if (do_kill) {
		Syslog('-', " Total Max. Days/Killed Max. Age/Killed Area name");
		Syslog('-', "------    ------ ------   ------ ------ ----------------------------------");
	}

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

	if (do_area) {
		if (fseek(pAreas, (msgshdr.recsize + msgshdr.syssize) * (do_area - 1), SEEK_CUR) == 0) {
			fread(&msgs, msgshdr.recsize, 1, pAreas);
			if (msgs.Active) {
				if (!do_quiet) {
					colour(3, 0);
					printf("\r%5ld .. %-40s", do_area, msgs.Name);
					fflush(stdout);
				}
				are_tot++;
				if (do_kill)
					KillArea();
				if (do_pack || msg_del)
					PackArea();
				if (do_index)
					IndexArea();
				if (do_link)
					LinkArea();
				if (processed)
					are_proc++;
			}
		}
	} else {
		arearec = 0;
		while (fread(&msgs, msgshdr.recsize, 1, pAreas) == 1) {
			fseek(pAreas, msgshdr.syssize, SEEK_CUR);
			arearec++;
			if (msgs.Active) {
				Nopper();
				if (!do_quiet) {
					colour(3, 0);
					printf("\r%5ld .. %-40s", arearec, msgs.Name);
					fflush(stdout);
				}
				are_tot++;
				processed = FALSE;
				if (do_kill)
					KillArea();
				if (do_pack || (Del != msg_del)) {
					PackArea();
				}
				Del = msg_del;
				if (do_index)
					IndexArea();
				if (do_link)
					LinkArea();
				if (processed)
					are_proc++;
			}
		}
	}
	fclose(pAreas);

	if (do_link)
		RemoveSema((char *)"msglink");

	die(0);
}



typedef struct {
	unsigned long	Subject;
	unsigned long	Number;
} MSGLINK;



void LinkArea()
{
	int		i, m;
	unsigned long	Number, Prev, Next, Crc, Total;
	char		Temp[128], *p;
	MSGLINK		*Link;
	
	IsDoing("Linking");

	if (Msg_Open(msgs.Base)) {
		if (!do_quiet) {
			colour(12, 0);
			printf(" (linking)");
			colour(13, 0);
			fflush(stdout);
		}

		if ((Total = Msg_Number()) != 0L) {
			if (Msg_Lock(30L)) {
				if ((Link = (MSGLINK *)malloc(Total * sizeof(MSGLINK))) != NULL) {
					memset(Link, 0, Total * sizeof(MSGLINK));
					Number = Msg_Lowest();
					i = 0;
					do {
						Msg_ReadHeader(Number);
						strcpy(Temp, Msg.Subject);
						p = strupr(Temp);
						if (!strncmp(p, "RE:", 3)) {
							p += 3;
							if (*p == ' ')
								p++;
						}
						Link[i].Subject = StringCRC32(p);
						Link[i].Number = Number;
						i++;

						if (CFG.slow_util && do_quiet && ((i % 3) == 0))
							usleep(1);

						if (((i % 10) == 0) && (!do_quiet)) {
							printf("%6d / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i, Total);
							fflush(stdout);
						}
					} while(Msg_Next(&Number) == TRUE);

					if (!do_quiet) {
						printf("%6d / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i, Total);
						fflush(stdout);
					}

					Number = Msg_Lowest();
					i = 0;
					do {
						Msg_ReadHeader(Number);
						Prev = Next = 0;
						Crc = Link[i].Subject;
	
						for (m = 0; m < Total; m++) {
							if (m == i)
								continue;
							if (Link[m].Subject == Crc) {
								if (m < i)
									Prev = Link[m].Number;
								else if (m > i) {
									Next = Link[m].Number;
									break;
								}
							}
						}

						if (CFG.slow_util && do_quiet && ((i % 3) == 0))
							usleep(1);

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

						if (Msg.Original != Prev || Msg.Reply != Next) {
							Msg.Original = Prev;
							Msg.Reply = Next;
							Msg_WriteHeader(Number);
							processed = TRUE;
							msg_link++;
						}

						i++;
					} while(Msg_Next(&Number) == TRUE);
	
					if (!do_quiet) {
						printf("%6d / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i, Total);
						fflush(stdout);
					}

					free(Link);
				}

				if (!do_quiet) {
					printf("               \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
					fflush(stdout);
				}
				Msg_UnLock();
			} else {
				Syslog('+', "Can't lock %s", msgs.Base);
			}
		}

		Msg_Close();

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



/*
 * Kill messages according to age and max messages.
 */
void KillArea()
{
	unsigned long	Number, TotalMsgs = 0, Highest, *Active, Counter = 0;
	int		i, DelCount = 0, DelAge = 0, Done;
	time_t		Today, MsgDate;

	IsDoing("Killing");
	Today = time(NULL) / 86400L;

	if (Msg_Open(msgs.Base)) {

		if (!do_quiet) {
			colour(12, 0);
			printf(" (Killing)");
			colour(13, 0);
			fflush(stdout);
		}

		if (Msg_Lock(30L)) {

			TotalMsgs = Msg_Number();

			if (TotalMsgs) {
				if ((Active = (unsigned long *)malloc((size_t)((TotalMsgs + 100L) * sizeof(unsigned long)))) != NULL) {
					i = 0;
					Number = Msg_Lowest();
					do {
						Active[i++] = Number;
					} while (Msg_Next(&Number) == TRUE);
				}
			} else
				Active = NULL;

			Number  = Msg_Lowest();
			Highest = Msg_Highest();

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

				if ((!do_quiet) && ((Counter % 10L) == 0)) {
					printf("%6lu / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", Counter, TotalMsgs);
					fflush(stdout);
				}
				if ((Counter % 10L) == 0)
					DoNop();

				Counter++;
				msg_tot++;

				if (Msg_ReadHeader(Number) == TRUE) {
					Done = FALSE;
					if (msgs.DaysOld) {
						MsgDate = Msg.Arrived / 86400L;

						if ((Today - MsgDate) > msgs.DaysOld) {
							Msg_Delete(Number);
							Done = TRUE;
							DelAge++;
							msg_del++;
							if (Active != NULL) {
								for (i = 0; i < TotalMsgs; i++) {
									if (Active[i] == Number)
										Active[i] = 0L;
								}
							}
						}
					}

					if (Done == FALSE && (msgs.MaxMsgs) && Msg_Number() > msgs.MaxMsgs) {
						Msg_Delete(Number);
						DelCount++;
						msg_del++;
						if (Active != NULL) {
							for (i = 0; i < TotalMsgs; i++) {
								if (Active[i] == Number)
									Active[i] = 0L;
							}
						}
					}
				} 
			} while (Msg_Next(&Number) == TRUE);

			if (Active != NULL)
				free(Active);
			Msg_UnLock();
		} else {
			Syslog('+', "Can't lock msgbase %s", msgs.Name);
		}

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

		Msg_Close();
		Syslog('-', "%6d    %6d %6d   %6d %6d %s", TotalMsgs, msgs.DaysOld, DelAge, msgs.MaxMsgs, DelCount, msgs.Name);

		if (!do_quiet) {
			printf("\b\b\b\b\b\b\b\b\b\b          \b\b\b\b\b\b\b\b\b\b");
			fflush(stdout);
		}
	} else
		Syslog('+', "Failed to open %s", msgs.Name);
}



void IndexArea()
{
}



/*
 * Pack message area if there are deleted messages.
 */
void PackArea()
{
	IsDoing("Packing");
	if (Msg_Open(msgs.Base)) {

		if (!do_quiet) {
			colour(12, 0);
			printf(" (Packing)");
			fflush(stdout);
		}

		if (Msg_Lock(30L)) {
			Msg_Pack();
			Msg_UnLock();
		} else
			Syslog('+', "Can't lock %s", msgs.Base);

		Msg_Close();

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

	if (CFG.slow_util && do_quiet)
		usleep(1);
}


