/*
 * 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/09/12 10:46:00 $
 */

extern const char *sma_optarg;
extern int sma_optind;

#include "sma.h"

int
main(int argc, char **argv) {
	FILE *fp = NULL;
	struct host *hptr;
	int c;

#ifdef _WIN32
	int getopt(int, char **, char *);
#endif

	/* initialize global variables: */
	hosts = 0;
	tval = time((time_t *)NULL);
	curr = localtime(&tval);
	first.next = NULL;
	pname = argv[0];

	/* get arguments: */
	while ((c = getopt(argc, argv, "C:D:H:L:b:f:l:r:t:o:O:AFacdhinpsqvw")) != -1)
		switch(c) {
			case 'A':
				dcaddrflag = 1;
				break;
			case 'D':
				Dflag = 1;
				Dchar = sma_optarg;
				break;
			case 'C':
				Cflag = 1;
				Cchar = sma_optarg;
				break;
			case 'F':
				Fflag = 1;
				break;
			case 'H':
				Hflag = 1;
				Hchar = sma_optarg;
				break;
			case 'a':
				aflag = 1;
				break;
			case 'b':
				bflag = 1;
				bchar = sma_optarg;
				break;
			case 'c':
				cflag = 1;
				break;
			case 'd':
				dflag = 1;
				break;
			case 'f':
				fflag = 1;
				fchar = sma_optarg;
				break;
			case 'h':
				hflag = 1;
				break;
			case 'i':
				iflag = 1;
				break;
			case 'l':
				lflag = 1;
				lnum = atoi(sma_optarg);
				break;
			case 'L':
				Lflag = 1;
				Lchar = sma_optarg;
				break;
			case 'r':
				rflag = 1;
				rnum = atoi(sma_optarg);
				break;
			case 'n':
				nflag = 1;
				break;
			case 'o':
				oflag = 1;
				ochar = sma_optarg;
				break;
			case 'O':
				Oflag = 1;
				Ochar = sma_optarg;
				break;
			case 'p':
				pflag = 1;
				break;
			case 's':
				sflag = 1;
				break;
			case 't':
				tflag = 1;
				tchar = sma_optarg;
				break;
			case 'q':
				qflag = 1;
				break;
			case 'v':
				vflag = 1;
				break;
			case 'w':
				wflag = 1;
				break;
			default:
				usage();
                }
        argc -= sma_optind;
        argv += sma_optind;

        /* Debug */
	if (vflag)
		fprintf(stderr, "%s: running in Debug mode\n", pname);

	/* Check argument logic: */

	if (hflag) usage();
	if (cflag) copying();
	if (!pgflag) pgflag = 1;

	/* Read in the configuration file: */
	if (fchar) {
		if (!(fp = fopen(fchar, "r"))) 
			fprintf(stderr, "%s: cannot open %s\n", pname, fchar);
		else {
			init(fp);
			(void)fclose(fp);
		}
	} else if (!Fflag) {
		if (!(fp = fopen(DEFAULT_CONF, "r"))) 
			fprintf(stderr, "%s: cannot open %s, using defaults\n", 
				pname, "configuration file");
		else {
			fchar = DEFAULT_CONF;
			init(fp);
			(void)fclose(fp);
		}
	}

	/* Hash table size: */
	if (!tflag || !strncmp(tchar, "normal", 6)) {
		asize = ASIZE_NORMAL;
		rsize = RSIZE_NORMAL;
	} else if (!strncmp(tchar, "big", 3)) {
		asize = ASIZE_BIG;
		rsize = RSIZE_BIG;
	} else if (!strncmp(tchar, "huge", 4)) {
		asize = ASIZE_HUGE;
		rsize = RSIZE_HUGE;
	} else {
		if (!(hsstring = strdup(tchar)))
			error_memory();
		hastring = hsstring;
		if (*hsstring == ',') {
			hastring = NULL;
			if (*(hsstring + 1) == '\0')
				hrstring = NULL;
			else
				hrstring = ++hsstring;
		}
		while (*hsstring != '\0') {
			if (*hsstring == ',') {
				*hsstring = '\0';
				if (*(hsstring + 1) == '\0')
					hrstring = NULL;
				else
					hrstring = ++hsstring;
				break;
			}
			hsstring++;
		}
		/* Address Hash table: */
		if (hastring != NULL)
			asize = atoi(hastring);
		else asize = ASIZE_NORMAL;
		/* Relay Hash table: */
		if (hrstring != NULL)
			rsize = atoi(hrstring);
		else rsize = RSIZE_NORMAL;

	}
	if (asize <= 0 || rsize <= 0) {
		fprintf(stderr, "%s: Illegal hash table size\n", pname);
		usage();
	}

	if (!Oflag) Oflag = FORMAT_ASCII;
	else if (!strncmp(Ochar, "ascii", 5)) Oflag = FORMAT_ASCII;
	else if (!strncmp(Ochar, "html", 4)) Oflag = FORMAT_HTML;
	else if (!strncmp(Ochar, "clog", 4)) Oflag = FORMAT_CLOG;
	else {
		fprintf(stderr, "%s: illegal output format \"%s\".\n",
			pname, Ochar);
		exit (1);
	}
	if (aflag) Oflag = FORMAT_ASCII;
	if (wflag) Oflag = FORMAT_HTML;

	if (!Cchar) Cchar = COMMENT;
	if (aflag) wflag = 0;
	if (!bchar) bchar = BG_COLOR;
	if (!tbchar) tbchar = TB_COLOR;
	if (!bechar) bechar = BOUNCE_ADDR;

	if (lflag) {
		if (lnum <= 0) lnum = 0;
		lrnum = lnum;
	} else if (!lrflag) {
		lnum = LDEF;
		if (lnum <= 0) lnum = 0;
		lrnum = LRDEF;
		if (lrnum <= 0) lrnum = 0;
	}
	if (rflag) {
		if (rnum <= 0) rnum = 0;
		rrnum = rnum;
	} else if (!rrflag) {
		rnum = RDEF;
		if (rnum <= 0) rnum = 0;
		rrnum = RRDEF;
		if (rrnum <= 0) rrnum = 0;
	}

	/* Set output: */
	if (ochar) {
		if (!(ofp = fopen(ochar, "w"))) {
			fprintf(stderr, "%s: cannot open %s\n", pname, ochar);
			ofp = stdout;
		}
	} else
		ofp = stdout;

	/* Check filter logic: */

	/* Envelope and relay filters: */
	if (sef || srf || ref || rrf) {
		if (!qflag) fprintf(stderr, "%s: setting filters\n", pname); 
#ifdef USE_REGEXP
		if (csflag)
			csflag = REG_EXTENDED|REG_NOSUB;
		else
			csflag = REG_EXTENDED|REG_ICASE|REG_NOSUB;
#endif
	}

	if (!sef || !strncmp(sef, "*", 1)) {
		sef = (char *)NULL;
	} else {
#ifdef USE_REGEXP
		if (*sef == '!') {
			if (*(sef+1) == '\0')
				sef = NULL;
			regcomp(&csef, sef+1, csflag);
		} else
			regcomp(&csef, sef, csflag);
#else
		if (*sef == '!' && *(sef+1) == '\0')
			sef = (char *)NULL;
#endif
		if (!qflag) fprintf(stderr, 
		"  envelope sender filter: %s\n", sef);
	}

	if (!srf || !strncmp(srf, "*", 1)) srf = (char *)NULL;
	else {
#ifdef USE_REGEXP
		if (*srf == '!') {
			if (*(srf+1) == '\0')
				srf = NULL;
			regcomp(&csrf, srf+1, csflag);
		} else
			regcomp(&csrf, srf, csflag);
#else
		if (*srf == '!' && *(srf+1) == '\0')
			srf = (char *)NULL;
#endif
		if (!qflag) fprintf(stderr, 
		"  relay sender filter: %s\n", srf);
	}

	if (!ref || !strncmp(ref, "*", 1)) ref = (char *)NULL;
	else {
#ifdef USE_REGEXP
		if (*ref == '!')
			regcomp(&cref, ref+1, csflag);
		else
			regcomp(&cref, ref, csflag);
#else
		if (*ref == '!' && *(ref+1) == '\0')
			ref = (char *)NULL;

#endif
		if (!qflag) fprintf(stderr, 
		"  envelope recipient filter: %s\n", ref);
	}

	if (!rrf || !strncmp(rrf, "*", 1)) rrf = (char *)NULL;
	else {
#ifdef USE_REGEXP
		if (*rrf == '!')
			regcomp(&crrf, rrf+1, csflag);
		else
			regcomp(&crrf, rrf, csflag);
#else
		if (*rrf == '!' && *(rrf+1) == '\0')
			rrf = (char *)NULL;
#endif
		if (!qflag) fprintf(stderr, 
		"  relay recipient filter: %s\n", rrf);
	}

	/* Start -and end times: */
	if (Dflag) {
		/* Read from command line.. */
		if (!(sstring = strdup(Dchar)))
			error_memory();
		tstring = sstring;
		if (*sstring == ',') {
			tstring = NULL;
			if (*(sstring + 1) == '\0')
				estring = NULL;
			else
				estring = ++sstring;
		}
		while (*sstring != '\0') {
			if (*sstring == ',') {
				*sstring = '\0';
				if (*(sstring + 1) == '\0')
					estring = NULL;
				else
					estring = ++sstring;
				break;
			}
			sstring++;
		}
		/* StartTime: */
		if (tstring != NULL)
			scan_time(&sstime, tstring);
		/* EndTime: */
		if (estring != NULL)
			scan_time(&eetime, estring);
	} else {
		if (!sstring || !(strncmp(sstring, "*", 1)))
			sstring = NULL;
		else if (sscanf(sstring, "%d/%d/%d-%d:%d:%d", 
	  	  &syear, &smonth, &sday, &shour, &sminute, &ssecond) != 6) {
			sstring = NULL;
			if (!qflag) {
				fprintf(stderr, "%s: illegal StartTime, "
				  "using (*)\n", pname);	
			}
		} else {
			sstime = (time_t)conv_time(smonth-1, sday, shour, 
				sminute, ssecond);
			if (!sstime && !qflag)
				fprintf(stderr, "%s: illegal StartTime, "
				  "using (*)\n", pname);	
		}
		if (!estring || !(strncmp(estring, "*", 1)))
			estring = NULL;
		else if (sscanf(estring, "%d/%d/%d-%d:%d:%d", 
		  &eyear, &emonth, &eday, &ehour, &eminute, &esecond) != 6) {
			if (!qflag) {
				fprintf(stderr, "%s: illegal EndTime, "
				  "using (*)\n", pname);	
			}
		} else {
			eetime = (time_t)conv_time(emonth-1, eday, ehour, 
				eminute, esecond);
			if (!eetime && !qflag)
				fprintf(stderr, "%s: illegal EndTime, "
				  "using (*)\n", pname);	
		}
	}
	/* Print out configuration to stdout: */
	if (pflag) {
		fprintf(stderr, "%s: configuration:\n", pname);
		dump_config(stdout);
		exit (0);
	}

	/* Print configuration in debug mode: */
	if (vflag) {
		fprintf(stderr, "%s: configuration:\n", pname);
		dump_config(stderr);
	}
	
	/* loop through remaining arguments (if any) and do parsing: */
	if (*argv)
		for (; *argv; ++argv) {
			if (!qflag) fprintf(stderr, 
				"%s: opening file %s\n", pname, *argv);
			if (!(fp = fopen(*argv, "r"))) 
				fprintf(stderr, 
					"%s: cannot open %s\n", pname, *argv);
			else {
				parse(fp, *argv);
				(void)fclose(fp);
			}
		}
	else {
		if (!qflag)
			fprintf(stderr, "%s: reading from stdin\n", pname);
		parse(stdin, "stdin");
	}

	/* If output format is clog, nothing to do here: */
	if (Oflag == FORMAT_CLOG)  exit (0);
	
	/* check host structure: */
	hptr = first.next;
	if (!hptr || !(hptr->inum) || !(hptr->onum)) {
		if (!qflag)
			fprintf(stderr, "%s: nothing to report\n", pname);
		exit(1);
	}
	for (hptr = first.next; hptr; hptr = hptr->next) {

		/* rearrange and sort: */
		sort(hptr);

		/* seconds between first and last log entry: */
		hptr->dtime = difftime(hptr->ltime, hptr->ftime);	

		/* first hour and day: */
		hptr->fday = (localtime(&hptr->ftime))->tm_wday;
		hptr->fhour = (localtime(&hptr->ftime))->tm_hour;

		/* time distributions: */
		average(hptr, hptr->idd, hptr->fidd, 
			hptr->ihh, hptr->fihh); 
		average(hptr, hptr->odd, hptr->fodd, 
			hptr->ohh, hptr->fohh);
	}

	/* print: */
	if (Oflag == FORMAT_HTML) html(ofp);
	else if (Oflag == FORMAT_ASCII) ascii(ofp);
	(void)fclose(ofp);

	/* OK: */
	exit(0);
}
