/*
 * sma -- Sendmail log analyser
 *
 * Copyright (c) 2000 - 2003 Jarkko Turkulainen. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY JARKKO TURKULAINEN ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL JARKKO TURKULAINEN BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Date: 2002/12/06 12:48:51 $
 */


/* Copyright for routines in scan_time() */

/*
 * Copyright (c) 1985, 1987, 1988, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "sma.h"

int isdigit(int);

/*
 * Take month as an ascii string and return integer
 */
int
conv_mon(const char *mon) {
	int k;
	const char *montab[] = {
		"Jan", "Feb", "Mar", "Apr", "May", 
		"Jun", "Jul", "Aug", "Sep", "Oct",
		"Nov", "Dec"
	};

	for (k = 0; k < 12; k++) {
		if (!strcmp(mon, montab[k]))
			break;
	}
	return k;
}

/*
 * Take month, day, hour, minute and second as integers
 * and return time_t
 */
time_t
conv_time(int mon, int day, int hh, int min, int sec) {

	/*
	 * Assume that the year is current year...
	 * This is bug.
	 */

	/* sanity check: */
	if (mon < 0 || mon >= 12) 
		return (time_t)NULL;

	/* fill the structure: */
	tp.tm_sec=sec;
	tp.tm_min=min;
	tp.tm_hour=hh;
	tp.tm_mday=day;
	tp.tm_mon=mon;
	tp.tm_year=curr->tm_year;
	tp.tm_isdst=-1;

	return mktime(&tp);
}

void
scan_time(time_t *utime, char *p) {
#define	ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;

	struct tm *lt;
	char *t, *dot;
	int bigyear;
	int yearset = 0;

	for (t = p, dot = NULL; *t; ++t) {
		if (isdigit(*t))
			continue;
		if (*t == '.' && dot == NULL) {
			dot = t;
			continue;
		}
		usage();
	}

	tval = time((time_t *)NULL);
        lt = localtime(&tval);

	lt->tm_isdst = -1;			/* correct for DST */

	if (dot != NULL) {			/* .SS */
		*dot++ = '\0';
		if (strlen(dot) != 2)
			usage();
		lt->tm_sec = ATOI2(dot);
		if (lt->tm_sec > 61)
			usage();
	} else
		lt->tm_sec = 0;

	switch (strlen(p)) {
	case 12:				/* cc */
		bigyear = ATOI2(p);
		lt->tm_year = bigyear * 100 - 1900;
		yearset = 1;
		/* FALLTHROUGH */
	case 10:				/* yy */
		if (yearset) {
			lt->tm_year += ATOI2(p);
		} else {
			lt->tm_year = ATOI2(p);
			if (lt->tm_year < 69)		/* hack for 2000 ;-} */
				lt->tm_year += (2000 - 1900);
			else
				lt->tm_year += (1900 - 1900);
		}
		/* FALLTHROUGH */
	case 8:					/* mm */
		lt->tm_mon = ATOI2(p);
		if ((lt->tm_mon > 12) || !lt->tm_mon)
			usage();
		--lt->tm_mon;			/* time struct is 0 - 11 */
		/* FALLTHROUGH */
	case 6:					/* dd */
		lt->tm_mday = ATOI2(p);
		if ((lt->tm_mday > 31) || !lt->tm_mday)
			usage();
		/* FALLTHROUGH */
	case 4:					/* HH */
		lt->tm_hour = ATOI2(p);
		if (lt->tm_hour > 23)
			usage();
		/* FALLTHROUGH */
	case 2:					/* MM */
		lt->tm_min = ATOI2(p);
		if (lt->tm_min > 59)
			usage();
		break;
	default:
		usage();
	}
	*utime = mktime(lt);
}

/*
 * Prepare a space for sorted address stuctures and do the sorting
 */
void
sort(struct host *hptr) {
	int i, j, k;
	struct envpair *eptr;
	struct in *iptr;
	struct out *optr;
	struct relpair *rptr;
	struct rin *riptr;
	struct rout *roptr;
	struct status *statptr;
	struct rule *ruleptr;
	struct rrelay *rrptr;

	/* space for sorted structures: */
	if (!(hptr->setab = malloc(hptr->edif * sizeof(struct envpair *))))
		error_memory();
	if (!(hptr->sitab = malloc(hptr->idif * sizeof(struct sin *))))
		error_memory();
	if (!(hptr->sotab = malloc(hptr->odif * sizeof(struct sout *))))
		error_memory();
	if (!(hptr->srtab = malloc(hptr->rrdif * sizeof(struct relpair *))))
		error_memory();
	if (!(hptr->rsitab = malloc(hptr->ridif * sizeof(struct rin *))))
		error_memory();
	if (!(hptr->rsotab = malloc(hptr->rodif * sizeof(struct rout *))))
		error_memory();
	if (!(hptr->ssttab = malloc(hptr->sdif * sizeof(struct status *))))
		error_memory();
	if (!(hptr->sruletab = malloc(hptr->rdif * sizeof(struct rule *))))
		error_memory();

	/* envelope pairs: */
	for (i = 0, k = 0; k < asize; k++) 
	  for (eptr = hptr->etab[k]; eptr; eptr = eptr->next)
			/* copy pointers: */
			hptr->setab[i++] = eptr;
	/* relay pairs: */
	for (i = 0, k = 0; k < rsize; k++) 
	  for (rptr = hptr->rtab[k]; rptr; rptr = rptr->next)
			/* copy pointers: */
			hptr->srtab[i++] = rptr;

	/* input: */
	for (i = 0, k = 0; k < asize; k++) 
	  for (iptr = hptr->itab[k]; iptr; iptr = iptr->next)
			/* copy pointers: */
			hptr->sitab[i++] = iptr;
	for (i = 0, k = 0; k < rsize; k++) 
	  for (riptr = hptr->ritab[k]; riptr; riptr = riptr->next)
			/* copy pointers: */
			hptr->rsitab[i++] = riptr;

	/* output: */
	for (i = 0, k = 0; k < asize; k++) 
	  for (optr = hptr->otab[k]; optr; optr = optr->next) 
			/* copy pointers: */
			hptr->sotab[i++] = optr;
	for (i = 0, k = 0; k < rsize; k++) 
	  for (roptr = hptr->rotab[k];roptr; roptr = roptr->next) 
			/* copy pointers: */
			hptr->rsotab[i++] = roptr;

	/* status and ruleset: */
	for (i = 0, k = 0; k < rsize; k++) 
	  for (statptr = hptr->sttab[k]; statptr; statptr = statptr->next) 
			/* copy pointers: */
			hptr->ssttab[i++] = statptr;

	for (i = 0, k = 0; k < rsize; k++) 
	  for (ruleptr = hptr->ruletab[k]; ruleptr; ruleptr = ruleptr->next) {

			/* copy pointers: */
			hptr->sruletab[i++] = ruleptr;

			/* alloc space for sorted relays: */
			if (!(ruleptr->srrelaytab =
			  malloc(ruleptr->reldif * sizeof(struct rrelay *))))
				error_memory();

	  		for (j = 0, rrptr = ruleptr->rrelaytab; rrptr;
			    rrptr = rrptr->next)
				ruleptr->srrelaytab[j++] = rrptr;

			/* sort */
			qsort(ruleptr->srrelaytab, ruleptr->reldif,
			  sizeof(struct rrelay *), comp_rrel);
	  }

	/* sort */
	qsort(hptr->setab, hptr->edif, sizeof(struct envpair *), comp_env);
	qsort(hptr->sitab, hptr->idif, sizeof(struct in *), comp_in);
	qsort(hptr->sotab, hptr->odif, sizeof(struct out *), comp_out);
	qsort(hptr->srtab, hptr->rrdif, sizeof(struct relpair *), comp_rel);
	qsort(hptr->rsitab, hptr->ridif, sizeof(struct rin *), comp_rin);
	qsort(hptr->rsotab, hptr->rodif, sizeof(struct rout *), comp_rout);
	qsort(hptr->ssttab, hptr->sdif, sizeof(struct status *), comp_s);
	qsort(hptr->sruletab, hptr->rdif, sizeof(struct rule *), comp_r);
}

/*
 * Comparing functions, called from sort()
 */
int
comp_env(const void *p1, const void *p2) {
	const struct envpair * sp1 = *(const struct envpair * const *)p1;
	const struct envpair * sp2 = *(const struct envpair * const *)p2;

	if (sflag) {
		if (sp1->size < sp2->size)
			return (1);
		if (sp1->size > sp2->size)
			return (-1);
	} else {
		if (sp1->num < sp2->num)
			return (1);
		if (sp1->num > sp2->num)
			return (-1);
	}
	return (0);
}
int
comp_rel(const void *p1, const void *p2) {
	const struct relpair * sp1 = *(const struct relpair * const *)p1;
	const struct relpair * sp2 = *(const struct relpair * const *)p2;

	if (sflag) {
		if (sp1->size < sp2->size)
			return (1);
		if (sp1->size > sp2->size)
			return (-1);
	} else {
		if (sp1->num < sp2->num)
			return (1);
		if (sp1->num > sp2->num)
			return (-1);
	}
	return (0);
}
int
comp_in(const void *p1, const void *p2) {
	const struct in * sp1 = *(const struct in * const *)p1;
	const struct in * sp2 = *(const struct in * const *)p2;

	if (sflag) {
		if (sp1->size < sp2->size)
			return (1);
		if (sp1->size > sp2->size)
			return (-1);
	} else {
		if (sp1->num < sp2->num)
			return (1);
		if (sp1->num > sp2->num)
			return (-1);
	}
	return (0);
}
int
comp_rin(const void *p1, const void *p2) {
	const struct rin * sp1 = *(const struct rin * const *)p1;
	const struct rin * sp2 = *(const struct rin * const *)p2;

	if (sflag) {
		if (sp1->size < sp2->size)
			return (1);
		if (sp1->size > sp2->size)
			return (-1);
	} else {
		if (sp1->num < sp2->num)
			return (1);
		if (sp1->num > sp2->num)
			return (-1);
	}
	return (0);
}

int
comp_out(const void *p1, const void *p2) {
	const struct out * sp1 = *(const struct out * const *)p1;
	const struct out * sp2 = *(const struct out * const *)p2;

	if (sflag) {
		if (sp1->size < sp2->size)
			return (1);
		if (sp1->size > sp2->size)
			return (-1);
	} else {
		if (sp1->num < sp2->num)
			return (1);
		if (sp1->num > sp2->num)
			return (-1);
	}
	return (0);
}
int
comp_rout(const void *p1, const void *p2) {
	const struct rout * sp1 = *(const struct rout * const *)p1;
	const struct rout * sp2 = *(const struct rout * const *)p2;

	if (sflag) {
		if (sp1->size < sp2->size)
			return (1);
		if (sp1->size > sp2->size)
			return (-1);
	} else {
		if (sp1->num < sp2->num)
			return (1);
		if (sp1->num > sp2->num)
			return (-1);
	}
	return (0);
}
int
comp_s(const void *p1, const void *p2) {
	const struct status * sp1 = *(const struct status * const *)p1;
	const struct status * sp2 = *(const struct status * const *)p2;

	if (sp1->num < sp2->num)
		return (1);
	if (sp1->num > sp2->num)
		return (-1);
	return (0);
}
int
comp_r(const void *p1, const void *p2) {
	const struct rule * sp1 = *(const struct rule * const *)p1;
	const struct rule * sp2 = *(const struct rule * const *)p2;

	if (sp1->num < sp2->num)
		return (1);
	if (sp1->num > sp2->num)
		return (-1);
	return (0);
}
int
comp_rrel(const void *p1, const void *p2) {
	const struct rrelay * sp1 = *(const struct rrelay * const *)p1;
	const struct rrelay * sp2 = *(const struct rrelay * const *)p2;

	if (sp1->num < sp2->num)
		return (1);
	if (sp1->num > sp2->num)
		return (-1);
	return (0);
}


/*
 * Calculate the average daily/hour messages
 */
void
average(struct host *hptr, int *dd, float *ad, int *hh, float *ah) {
	int fulld;
	int fullh;
	int weeks;
	int hours;
	int hfday, hfhour;
	int i;

	hfday = hptr->fday;
	hfhour = hptr->fhour;

	/* integer value of hours: */
	fullh = (int)hptr->dtime/3600;
	fullh++;

	/* integer value of days: */
	fulld = (int)hptr->dtime/(24*3600);
	fulld++;

	/* 
	 * calculate full weeks: 
	 *
	 * days left over from last full week are returned
	 * as int fulld:
 	 */
	weeks = 0;
	for (; fulld >= 7; fulld -= 7) 
		weeks++;

	/* calculate daily averages: */

	for (i = 0; i < 7; i++) 
			if (i == hfday && fulld > 0) {
				ad[i] = (float)dd[i] / ((float)weeks + 1.0);
				fulld--;
				hfday++;
			} else 
				if (weeks)
					ad[i] = (float)dd[i] / (float)weeks;
				else
					ad[i] = (float)dd[i];


	/* same calculations for hours: */

	hours = 0;
	for (; fullh >= 24; fullh -= 24) 
		hours++;
	for (i = 0; i < 24; i++) 
			if (i == hfhour && fullh > 0) {
				ah[i] = (float)hh[i] / ((float)hours + 1.0);
				fullh--;
				hfhour++;
			} else 
				if (hours)
					ah[i] = (float)hh[i] / (float)hours;
				else
					ah[i] = (float)hh[i];

}

/* Strip newline: */

char *
stripn(char *s) {
	char *p;
	
	for (p = s; *s != '\n'; s++)	
		;
	*s = '\0';
	return p;
}

void
error_memory() {
	fprintf(stderr, "%s: memory allocation failure, errno: %d\n",
		pname, errno);
	exit(1);
}

void
copying() {
	fprintf(stderr, "\nSMA version %s\n\n" , VERSION);
	fprintf(stderr, "This program is Copyright (c) 2000 - 2003\n"
			"      Jarkko Turkulainen. All rights reserved.\n"
			"Some parts are "
			"Copyright (c) 1992, 1993, 1994\n"
			"      Henry Spencer.  All rights reserved.\n"
			"Some parts are "
			"Copyright (c) 1985, 1987, 1988, 1993, 1994\n"
			"      The Regents of the University of California. "
			"All rights reserved.\n\n");
	exit(0);
}

void
usage() {
	/* Use stdout - easier to "more" or "less" .. */
	fprintf(stdout, "\nSMA version %s\n\n" , VERSION);
	fprintf(stdout, "usage: %s [OPTIONS] [files...]\n"
	"OPTIONS are\n"
	"    -A\t\tforce addresses to lower case to help with consistent counts\n"
	"    -a\t\tset the output format as ascii\n"
	"    -b <rgb>\tbackground color (RGB) of the HTML form\n"
	"    -c\t\tshow copyright information and exit\n"
	"    -C <string>\tset report header as <string>\n"
	"    -D <d1,d2>\tanalyse only log entries between dates d1 and d2\n"
	"    -d\t\tprint only the domain portion of email addresses\n"
	"    -f <file>\tuse configuration from <file> instead of default\n"
	"    -F\t\tdo not use default configuration file even if it exists\n"
	"    -h\t\tprint this text and exit\n"
	"    -H <name>\toverride the hostname with <name>\n"
	"    -i\t\tprint ASCII report as HTML comment (requires -w or -O html)\n"
	"    -L <string>\tprocess only lines with syslog tag <string>\n"
	"    -n\t\tdo not print the time distribution\n"
	"    -o <file>\twrite output to <file>\n"
	"    -O <format>\tset output format (ascii/html/clog)\n"
	"    -p\t\tprint current configuration to stdout\n"
	"    -s\t\tsort by tranfers\n"
	"    -q\t\tbe quiet (no error messages)\n"
	"    -v\t\tshow debugging information\n"
	"    -l <num>\tprint <num> top addresses\n"
	"    -r <num>\tprint <num> top relay addresses\n"
	"    -t <value>\tset the internal hash table size (normal/big/huge/a,r)\n"
	"    -w\t\tset the output format as html\n"
	"    files\tlog files\n\n", pname);
	exit(0);
}

void
dump_config(FILE *handle) {
	char *p = NULL;
	char dates[128];

	fprintf(handle, "  BgColor %s\n", bchar);
	fprintf(handle, "  BounceAddress %s\n", bechar);
	fprintf(handle, "  CaseSensitive %s\n", csflag ? "no" : "yes");
	if (cfchar)
		fprintf(handle, "  ClogFormat \"%s\"\n", cfchar);
	fprintf(handle, "  ClogSentOnly %s\n", clsflag ? "yes" : "no");
	fprintf(handle, "  Comment \"%s\"\n", Cchar);
	fprintf(handle, "  Debug %s\n", vflag ? "yes" : "no");
	if (eetime) {
		strftime(dates, 127, "%Y/%m/%d-%H:%M:%S", localtime(&eetime));
		fprintf(handle, "  EndTime %s\n", dates);
	}
	fprintf(handle, "  EnvelopePairs %d\n", epnum);
	fprintf(handle, "  EnvelopeRecipientFilter %s\n", ref ? ref : "*");
	fprintf(handle, "  EnvelopeRecipients %d\n", lrnum);
	fprintf(handle, "  EnvelopeSenderFilter %s\n", sef ? sef : "*");
	fprintf(handle, "  EnvelopeSenders %d\n", lnum);
	if (ftchar)
		fprintf(handle, "  FooterText \"%s\"\n", ftchar);
	switch(Oflag) {
		case FORMAT_ASCII:
			if (!(p = strdup("ascii")))
				error_memory();
			break;
		case FORMAT_HTML:
			if (!(p = strdup("html")))
				error_memory();
			break;
		case FORMAT_CLOG:
			if (!(p = strdup("clog")))
				error_memory();
			break;
	}
	fprintf(handle, "  Format %s\n", p);
	free(p);
	fprintf(handle, "  HashTables %d,%d\n", asize, rsize);
	if (htchar)
		fprintf(handle, "  HeaderText \"%s\"\n", htchar);
	if (Hchar)
		fprintf(handle, "  HostName \"%s\"\n", Hchar);
	fprintf(handle, "  IncludeAscii %s\n", iflag ? "yes" : "no");
	if (ochar)
		fprintf(handle, "  OutFile \"%s\"\n", ochar);
	if (plchar)
		fprintf(handle, "  PictureLink \"%s\"\n", plchar);
	if (ppchar)
		fprintf(handle, "  PictureParameters \"%s\"\n", ppchar);
	if (pachar)
	fprintf(handle, "  PictureURL %s\n", pachar);
	fprintf(handle, "  PrintGeneralInfo %s\n", pgflag ? "yes" : "no");
	fprintf(handle, "  PrintRuleset %s\n", rsnum ? "yes" : "no");
	fprintf(handle, "  PrintStatus %s\n", stnum ? "yes" : "no");
	fprintf(handle, "  RelayPairs %d\n", rpnum);
	fprintf(handle, "  RelayRecipientFilter %s\n", rrf ? rrf : "*");
	fprintf(handle, "  RelayRecipients %d\n", rrnum);
	fprintf(handle, "  RelaySenderFilter %s\n", srf ? srf : "*");
	fprintf(handle, "  RelaySenders %d\n", rnum);
	fprintf(handle, "  RulesetRelays %d\n", rsrnum);
	fprintf(handle, "  ShowUsers %s\n", dflag ? "no" : "yes");
	fprintf(handle, "  Silent %s\n", qflag ? "yes" : "no");
	fprintf(handle, "  Sorting %s\n", sflag ? "transfer" : "number");
	if (sstime) {
		strftime(dates, 127, "%Y/%m/%d-%H:%M:%S", localtime(&sstime));
		fprintf(handle, "  StartTime %s\n", dates);
	}
	if (Lchar)
		fprintf(handle, "  SyslogTag %s\n", Lchar);
	fprintf(handle, "  TbColor %s\n", tbchar);
}
