#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "array.h"
#include "intcnt.h"
extern "C" {
#include "error.h"
}
#include "utils.h"

#define	MINUTE_STEP	5	/* interval for displaying per-minute info */
#define	SECOND_STEP	5	/* interval for displaying per-second info */

class procdata
{
public:
	intcnt month;
	intcnt dayofmonth;
	intcnt hour;
	intcnt minute;
	intcnt second;

	time_t prevts;
	intcnt freqs;

	char *procname;
};

procdata **procs = NULL;
int nprocs = 0;

char *months[] = {"Jan", "Feb", "Mar", "Apr", "May",
		  "Jun", "Jul", "Aug", "Sep", "Oct",
		  "Nov", "Dec"};

time_t gen_time_t(int year, int month, int day, int hour, int minute, int seconds)
{
	struct tm then;

	memset(&then, 0x00, sizeof(then));

	then.tm_year = year;
	then.tm_mon  = month;
	then.tm_mday = day;
	then.tm_hour = hour;
	then.tm_min  = minute;
	then.tm_sec  = seconds;

	return mktime(&then);
}

int split_line(char *in, int *year, int *month, int *day,
			  int *hour, int *minute, int *seconds,
			  char **proc,
			  char **line)
{
	char rc = 0;
	char *fields[6];
	char *copy = mystrdup(in);
	int loop;

	/* format:
	 * Mmm dd  hh:mm:ss host proc[pid]: line
	 * examples:
         * Oct 24 14:44:46 muur snort: spp_portscan: PORTSCAN DETECTED to port 4365 from 195.241.135.194 (STEALTH)
	 * Oct 24 14:44:52 muur smbd[3059]: [2003/10/24 14:44:52, 0] smbd/oplock_linux.c:linux_init_kernel_oplocks(287)
	 */

	/* split line up into fields */
	fields[0] = copy;

	for(;;)
	{
		char *dummy;

		for(loop=1; loop<6; loop++)
		{
			dummy = strchr(fields[loop-1] + 1, ' ');
			if (!dummy)
			{
				rc = -1;
				break;
			}
			*dummy = 0x00;
			dummy++;
			while(*dummy == ' ')
				dummy++;
			fields[loop] = dummy;
		}
		if (rc == -1) break;

		/* year unknown */
		*year = 0;

		/* month */
		for(*month=0; *month < 12; (*month)++)
		{
			if (strcmp(fields[0], months[*month]) == 0)
				break;
		}
		if (*month == 12)
		{
			rc = -1;
			break;
		}

		/* day */
		*day = atoi(fields[1]);

		/* time */
		*hour = atoi(fields[2]);
		*minute = atoi(&fields[2][3]);
		*seconds = atoi(&fields[2][6]);

		/* procname */
		dummy = strchr(fields[4], '[');
		if (dummy)
			*dummy = 0x00;
		dummy = strchr(fields[4], ':');
		if (dummy)
			*dummy = 0x00;

		*proc = mystrdup(fields[4]);

		/* description */
		*line = mystrdup(&copy[fields[5] - fields[0]]);

		break;
	}

	free(copy);

	return rc;
}

int process_line(char *in)
{
	int rc = 0;
	char *proc = NULL, *line = NULL;
	time_t now = time(NULL);
	struct tm *curtm = localtime(&now);

	for(;;)
	{
		int year, month, day, hour, minute, seconds;
		time_t curts;
		procdata *curproc = NULL;
		int loop;

		/* ignore '-- MARK --' lines */
		if (strcmp(&in[strlen(in) - 10], "-- MARK --") == 0)
			break;

		/* ignore "last message repeated x times" lines */
		if (strstr(in, "last message repeated ") != NULL && strcmp(&in[strlen(in) - 6], " times") == 0)
			break;

		/* split line in fields */
		if (split_line(in, &year, &month, &day, &hour, &minute,
			&seconds, &proc, &line) != 0)
		{
			rc = -1;
			break;
		}

		/* gen timestamp */
		year = curtm -> tm_year;
		curts = gen_time_t(year, month, day, hour, minute, seconds);

		/* update statistics if already known */
		for(loop=0; loop<nprocs; loop++)
		{
			if (strcmp(procs[loop] -> procname, proc) == 0)
			{
				curproc = procs[loop];

				/* calculate most popular interval */
				(curproc -> freqs).add(curts - curproc -> prevts);
				curproc -> prevts = curts;
				break;
			}
		}

		/* add if new */
		if (loop == nprocs)
		{
			nprocs++;
			procs = (procdata **)myrealloc(procs, nprocs * sizeof(procdata *), "procdata *");
			procs[nprocs - 1] = new procdata;
			if (!procs[nprocs-1])
				error_exit("cannot allocate procdata object");
			curproc = procs[nprocs - 1];
			
			curproc -> prevts = curts;
			curproc -> procname = strdup(proc);
		}

		(curproc -> month).add(month);
		(curproc -> dayofmonth).add(day);
		(curproc -> hour).add(hour);
		(curproc -> minute).add(minute);
		(curproc -> second).add(seconds);

		break;
	}

	free(proc);
	free(line);

	return rc;
}

void secondstostring(time_t ts, char *to)
{
	sprintf(to, "%02d:%02d:%02d", ts / 3600, (ts / 60) % 60, ts % 60);
}

void show_frequency_tables(void)
{
	int loop;

	printf("<H2>Frequencies</H2>\n");

	printf("<TABLE BORDER=\"1\">\n");
	printf("<TR><TD><B>Process</B></TD>");
	for(loop=0; loop<5; loop++)
		printf("<TD><B>Interval / Frequency</B></TD>");
	printf("</TR>\n");
	for(loop=0; loop<nprocs; loop++)
	{
		int loop2;

		printf("<TR><TD><B>%s</B></TD>", procs[loop]-> procname);
		procs[loop] -> freqs.sort();
		for(loop2=0; loop2<min(5, procs[loop] -> freqs.getn()); loop2++)
		{
			char buffer[128];
			secondstostring(procs[loop] -> freqs.getvalue(loop2), buffer);
			printf("<TD>%s / %d</TD>", buffer, procs[loop] -> freqs.getcnt(loop2));
		}
		printf("</TR>\n");
	}
	printf("</TABLE>\n");
}

void show_topx_tables(void)
{
	int loop;

	printf("<H2>Top10 tables</H2>\n");

	/* count */
	printf("<H3>Months</H3>\n");
	printf("<TABLE BORDER=\"1\">\n");
	printf("<TR><TD><B>Month</B></TD>");
	for(loop=0; loop<5; loop++)
		printf("<TD><B>Process [count]</B></TD>");
	printf("</TR>\n");
	for(int month=0; month<12; month++)
	{
		array *amonth = new array(1);

		for(loop=0; loop<nprocs; loop++)
			amonth -> addstring(procs[loop] -> procname, procs[loop] -> month.getcntfromvalue(month));
		amonth -> sort(0);

		printf("<TR><TD><B>%s</B></TD>", months[month]);
		for(loop=0; loop<min(nprocs, 5); loop++)
			if (amonth -> getcounter(loop, 0) == -1)
				printf("<TD></TD>");
			else
				printf("<TD>%s [%d]</TD>", amonth -> getstring(loop), amonth -> getcounter(loop, 0));
		printf("</TR>\n");

		delete amonth;
	}
	printf("</TABLE>\n");
	printf("<BR>\n");

	printf("<H3>Day of month</H3>\n");
	printf("<TABLE BORDER=\"1\">\n");
	printf("<TR><TD><B>Day</B></TD>");
	for(loop=0; loop<5; loop++)
		printf("<TD><B>Process [count]</B></TD>");
	printf("</TR>\n");
	for(int day=0; day<31; day++)
	{
		array *adayofmonth = new array(1);

		for(loop=0; loop<nprocs; loop++)
			adayofmonth -> addstring(procs[loop] -> procname, procs[loop] -> dayofmonth.getcntfromvalue(day));
		adayofmonth -> sort(0);

		printf("<TR><TD><B>%d</B></TD>", day + 1);
		for(loop=0; loop<min(nprocs, 5); loop++)
			if (adayofmonth -> getcounter(loop, 0) == -1)
				printf("<TD></TD>");
			else
				printf("<TD>%s [%d]</TD>", adayofmonth -> getstring(loop), adayofmonth -> getcounter(loop, 0));
		printf("</TR>\n");

		delete adayofmonth;
	}
	printf("</TABLE>\n");
	printf("<BR>\n");

	printf("<H3>Hour</H3>\n");
	printf("<TABLE BORDER=\"1\">\n");
	printf("<TR><TD><B>Hour</B></TD>");
	for(loop=0; loop<5; loop++)
		printf("<TD><B>Process [count]</B></TD>");
	printf("</TR>\n");
	for(int hour=0; hour<24; hour++)
	{
		array *ahour = new array(1);

		for(loop=0; loop<nprocs; loop++)
			ahour -> addstring(procs[loop] -> procname, procs[loop] -> hour.getcntfromvalue(hour));
		ahour -> sort(0);

		printf("<TR><TD><B>%d</B></TD>", hour+1);
		for(loop=0; loop<min(nprocs, 5); loop++)
			if (ahour -> getcounter(loop, 0) == -1)
				printf("<TD></TD>");
			else
				printf("<TD>%s [%d]</TD>", ahour -> getstring(loop), ahour -> getcounter(loop, 0));
		printf("</TR>\n");

		delete ahour;
	}
	printf("</TABLE>\n");
	printf("<BR>\n");

	printf("<H3>Minute</H3>\n");
	printf("<TABLE BORDER=\"1\">\n");
	printf("<TR><TD><B>Minute</B></TD>");
	for(loop=0; loop<5; loop++)
		printf("<TD><B>Process [count]</B></TD>");
	printf("</TR>\n");
	for(int minute=0; minute<60; minute += MINUTE_STEP)
	{
		array *aminute = new array(1);
		int dummy;

		for(dummy=0; dummy<MINUTE_STEP; dummy++)
		{
			for(loop=0; loop<nprocs; loop++)
				aminute -> addstring(procs[loop] -> procname, procs[loop] -> minute.getcntfromvalue(minute + dummy));
		}
		aminute -> sort(0);

		printf("<TR><TD><B>%d</B></TD>", minute);
		for(loop=0; loop<min(nprocs, 5); loop++)
			if (aminute -> getcounter(loop, 0) == -1)
				printf("<TD></TD>");
			else
				printf("<TD>%s [%d]</TD>", aminute -> getstring(loop), aminute -> getcounter(loop, 0));
		printf("</TR>\n");

		delete aminute;
	}
	printf("</TABLE>\n");
	printf("<BR>\n");

	/* let's not do per-second, it ain't that intresting */
}

int main(int argc, char *argv[])
{
	int fd = 0;

	/* open file if filename is given */
	if (argc == 2)
	{
		fd = open(argv[1], O_RDONLY);
		if (fd == -1)
		{
			fprintf(stderr, "Failed to open file %s, reason: %s\n", argv[1], strerror(errno));
			return 1;
		}
	}

	buffered_reader	br(fd);

	/* read & parse logfile */
	for(;;)
	{
		char *line = br.read_line();
		if (!line)	/* EOF */
			break;

		(void)process_line(line);

		free(line);
	}

	/* now generate them statistics */

	/* show headers */
	printf("<HTML><BODY><H1>");
	if (argc == 2)
		printf("Statistics for '%s'", argv[1]);
	else
		printf("Syslog statistics");
	printf("</H1>\n");

	/* frequency table */
	show_frequency_tables();

	/* tables per month/year/etc. */
	show_topx_tables();

	/* show trailer */
	printf("<BR>\n");
	printf("<HR>\n");
	printf("slst, (C) by folkert@vanheusden.com\n");
	printf("</BODY></HTML>\n");

	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1