/*
 * 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: 2003/04/03 12:42:33 $
 */


#include "sma.h"


/* hash function, stolen from O'Reilly book */

unsigned
hash(const char *s, int size) {
	unsigned val;

	val = 0;
	while (*s != '\0') {
		int tmp;
		val = (val << 4) + (*s);
		if ((tmp = (val & 0xf0000000U))) {
			val = val ^ (tmp >> 24);
			val = val ^ tmp;
		}
		s++;
	}
	return val % size;
}

/* initialize host struct: */

struct host *
init_host(const char *s) {
	struct host *ptr;

	for (ptr = first.next; ptr; ptr = ptr->next)
		if (!(strcmp(s, ptr->name))) 
			return ptr;
	hosts++;
	/* space: */
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->name = strdup(s)))
		error_memory();
	
	/* inital values: */
	if (!(ptr->etab = malloc(asize * sizeof(struct envpair *))))
                error_memory();
	memset(ptr->etab, 0, sizeof(struct envpair *) * asize);
	if (!(ptr->itab = malloc(asize * sizeof(struct in *))))
                error_memory();
	memset(ptr->itab, 0, sizeof(struct in *) * asize);
	if (!(ptr->otab = malloc(asize * sizeof(struct out *))))
                error_memory();
	memset(ptr->otab, 0, sizeof(struct out *) * asize);
	if (!(ptr->rtab = malloc(rsize * sizeof(struct envpair *))))
                error_memory();
	memset(ptr->rtab, 0, sizeof(struct envpair *) * rsize);
	if (!(ptr->ritab = malloc(rsize * sizeof(struct rin *))))
                error_memory();
	memset(ptr->ritab, 0, sizeof(struct rin *) * rsize);
	if (!(ptr->rotab = malloc(rsize * sizeof(struct rout *))))
                error_memory();
	memset(ptr->rotab, 0, sizeof(struct rout *) * rsize);
	if (!(ptr->sttab = malloc(rsize * sizeof(struct status *))))
                error_memory();
	memset(ptr->sttab, 0, sizeof(struct status *) * rsize);
	if (!(ptr->ruletab = malloc(rsize * sizeof(struct rule *))))
                error_memory();
	memset(ptr->ruletab, 0, sizeof(struct rule *) * rsize);
	ptr->msgidtab = NULL;
	ptr->omsgidtab = NULL;
	ptr->alias = 0;
	ptr->hopc = 0;
	ptr->lcerror = 0;
	ptr->oserror = 0;
	ptr->dstart = 0;
	memset(ptr->ihh, 0, sizeof(int) * 24);
	memset(ptr->fihh, 0, sizeof(float) * 24);
	memset(ptr->idd, 0, sizeof(int) * 7);
	memset(ptr->fidd, 0, sizeof(float) * 7);
	memset(ptr->ohh, 0, sizeof(int) * 24);
	memset(ptr->fohh, 0, sizeof(float) * 24);
	memset(ptr->odd, 0, sizeof(int) * 7);
	memset(ptr->fodd, 0, sizeof(float) * 7);
	ptr->inum = 0;
	ptr->onum = 0;
	ptr->gonum = 0;
	ptr->rinum = 0;
	ptr->ronum = 0;
	ptr->sent = 0;
	ptr->queu = 0;
	ptr->hunk = 0;
	ptr->uunk = 0;
	ptr->service = 0;
	ptr->other = 0;
	ptr->defe = 0;
	ptr->rule = 0;
	ptr->size = 0;
	ptr->isize = 0;
	ptr->osize = 0;
	ptr->lsize = 0;
	ptr->edif = 0;
	ptr->idif = 0;
	ptr->odif = 0;
	ptr->rrdif = 0;
	ptr->ridif = 0;
	ptr->rodif = 0;
	ptr->sdif = 0;
	ptr->rdif = 0;
	ptr->fhost = 0;

	/* utilize next pointer: */
	ptr->next = first.next;
	first.next = ptr;
	return ptr;
}


/* update input struct: */

void
update_in(struct host *hptr, const char *s, int ssize) {
	unsigned hashval;
	struct in *ptr;

	for (ptr = hptr->itab[hash(s,asize)]; ptr; ptr = ptr->next)
		if (!strcmp(s, ptr->name)) {
			ptr->size += ssize;
			ptr->num++;
			return; 
		}
	/* number of _different_ addresses: */
	hptr->idif++;

	/* space: */
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->name = strdup(s)))
		error_memory();

	/* initial values: */
	ptr->num = 1;
	ptr->size = ssize;

	/* space for next one: */
	hashval = hash(s,asize);
	ptr->next = hptr->itab[hashval];
	hptr->itab[hashval] = ptr;

}

/* remove input structure */
void
remove_in(struct host *hptr, const char *s, int size) {
	unsigned hashval;
	struct in *ptr, *prev;

	if (vflag) fprintf(stderr, "  removing %s... ", s);

	hashval = hash(s,asize);
	prev = NULL;
	for (ptr = hptr->itab[hashval]; ptr; ptr = ptr->next) {
		if (!strcmp(s, ptr->name)) {
			ptr->num--;
			ptr->size -= size;
			if (ptr->num) {
				if (vflag) fprintf(stderr, "OK\n");
				return;
			}
			if (prev == NULL)
				hptr->itab[hashval] = ptr->next;
			else
				prev->next = ptr->next;
			free(ptr->name);
			free(ptr);
			if (vflag) fprintf(stderr, "OK\n");
			hptr->idif--;
			return;
		}
		prev = ptr;
	}
	if (vflag) fprintf(stderr, "not found\n");
}


/* update output struct: */

void
update_out(struct host *hptr, const char *s, int ssize) {
	unsigned hashval;
	struct out *ptr;

	for (ptr = hptr->otab[hash(s,asize)];ptr; ptr = ptr->next)
		if (!strcmp(s, ptr->name)) {
			ptr->size += ssize;
			ptr->num++;
			return; 
		}
	hptr->odif++;
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->name = strdup(s)))
		error_memory();
	ptr->num = 1;
	ptr->size = ssize;
	hashval = hash(s,asize);
	ptr->next = hptr->otab[hashval];
	hptr->otab[hashval] = ptr;
}

/* update input relay struct: */

void
update_rin(struct host *hptr, const char *s, int ssize) {
	unsigned hashval;
	struct rin *ptr;

	if (vflag) fprintf(stderr, "  updating input relay %s... ", s);

	for (ptr = hptr->ritab[hash(s,rsize)];ptr;ptr = ptr->next)
		if (!strcmp(s, ptr->name)) {
			ptr->size += ssize;
			ptr->num++;
			if (vflag) fprintf(stderr, "OK\n");
			return; 
		}
	if (vflag) fprintf(stderr, "not found, adding.\n");
	hptr->ridif++;
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->name = strdup(s)))
		error_memory();
	ptr->size = ssize;
	ptr->num = 1;
	hashval = hash(s,rsize);
	ptr->next = hptr->ritab[hashval];
	hptr->ritab[hashval] = ptr;

}
/* remove input relay structure */
void
remove_rin(struct host *hptr, const char *s, int size) {
	unsigned hashval;
	struct rin *ptr, *prev;

	if (vflag) fprintf(stderr, "  removing %s... ", s);

	hashval = hash(s,rsize);
	prev = NULL;
	for (ptr = hptr->ritab[hashval]; ptr ; ptr = ptr->next) {
		if (!strcmp(s, ptr->name)) {
			ptr->num--;
			ptr->size -= size;
			if (ptr->num) {
				if (vflag) fprintf(stderr, "OK\n");
				return;
			}
			if (prev == NULL)
				hptr->ritab[hashval] = ptr->next;
			else
				prev->next = ptr->next;
			free(ptr->name);
			free(ptr);
			if (vflag) fprintf(stderr, "OK\n");
			hptr->ridif--;
			return;
		}
		prev = ptr;
	}
	if (vflag) fprintf(stderr, "not found\n");
}

/* update output relay struct: */

void
update_rout(struct host *hptr, const char *s, int ssize) {
	unsigned hashval;
	struct rout *ptr;

	if (vflag) fprintf(stderr, "  updating output relay %s... ", s);

	for (ptr = hptr->rotab[hash(s,rsize)]; ptr; ptr=ptr->next)
		if (!strcmp(s, ptr->name)) {
			ptr->num++;
			ptr->size += ssize;
			if (vflag) fprintf(stderr, "OK\n");
			return; 
		}
	if (vflag) fprintf(stderr, "not found, adding.\n");
	hptr->rodif++;
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->name = strdup(s)))
		error_memory();
	ptr->num = 1;
	ptr->size = ssize;
	hashval = hash(s,rsize);
	ptr->next = hptr->rotab[hashval];
	hptr->rotab[hashval] = ptr;
}

/* update message ID structure */

int
update_msgid(struct host *hptr, const char *p, const char *q, const char *s, 
		int m, int k, int n, int size, const char *msg) {
	struct msgid *ptr;
	
	if (vflag) fprintf(stderr, "  initializing msgid %s, chain: ", s);
	for (ptr = hptr->msgidtab; ptr; ptr=ptr->next) {
		if (!strcmp(s, ptr->id)) {
			if (vflag) fprintf(stderr, "msgid found!\n");
			return(0);
		}
		if (vflag) fprintf(stderr, "%s, ", ptr->id);
	}
	if (vflag) fprintf(stderr, "\n");
	
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->sender = strdup(p)))
		error_memory();
	if (!(ptr->relay = strdup(q)))
		error_memory();
	if (!(ptr->id = strdup(s)))
		error_memory();
	if (!(ptr->msgid = strdup(msg)))
		error_memory();
	/* Set delivery flag as "NOT_DELIVERED": */
	ptr->flag = 0;
	ptr->hh = m;
	ptr->day = k;
	ptr->num = n;
	ptr->size = size;

	/* utilize next pointer: */
	ptr->next = hptr->msgidtab;
	hptr->msgidtab = ptr;
	return(0);
}

/* get current message ID structure */

struct msgid *
get_msgid(struct host *hptr, const char *s) {
	struct msgid *ptr;

	if (vflag) fprintf(stderr, "  getting size for msgid %s...", s);
	
	for (ptr = hptr->msgidtab; ptr; ptr=ptr->next)
		if (!strcmp(s, ptr->id)) {
			if (vflag) fprintf(stderr, " %e\n", (double)ptr->size);
			return(ptr);
		}
	if (vflag) fprintf(stderr, " not found\n");
	return(NULL);
}

/* check message ID structure */

void
check_msgid(struct host *hptr, const char *s, int p) {
	struct msgid *ptr, *prev;

	if (!p && vflag)
		fprintf(stderr, "  checking %s... ", s);
	
	prev = NULL;
	for (ptr = hptr->msgidtab; ptr; ptr=ptr->next) {
		if (!strcmp(s, ptr->id)) {
			if (!p) {
				ptr->num--;
				if (vflag) fprintf(stderr,
				  "nr of recipients left: %d\n", ptr->num);
			}
			if (ptr->num < 0)
				ptr->num = 0;
				
			if (p && ptr->num == 0) {
				if (vflag) fprintf(stderr, 
				  "  %s removed from chain\n", s);
				if (prev == NULL)
					hptr->msgidtab = ptr->next;
				else
					prev->next = ptr->next;
				free(ptr->id);
				free(ptr->sender);
				free(ptr->relay);
				free(ptr);
			}
			return;
		}
		prev = ptr;
	}
	if (!p && vflag)
		fprintf(stderr, "not found\n");
}
/* remove message ID structure */
void
remove_msgid(struct host *hptr, const char *s) {
	struct msgid *ptr, *prev;

	if (vflag) fprintf(stderr, "  removing %s... ", s);

	prev = NULL;
	for (ptr = hptr->msgidtab; ptr; ptr=ptr->next) {
		if (!strcmp(s, ptr->id)) {
			if (vflag) fprintf(stderr, "OK\n");
			if (prev == NULL)
				hptr->msgidtab = ptr->next;
			else
				prev->next = ptr->next;
			free(ptr->id);
			free(ptr->sender);
			free(ptr->relay);
			free(ptr);
			return;
		}
		prev = ptr;
	}
	if (vflag) fprintf(stderr, "not found\n");
}

/* update out-of-order message ID structure */

int
update_omsgid(struct host *hptr, const char *s, int n) {
	struct omsgid *ptr;
	
	if (vflag) fprintf(stderr, "  initializing omsgid %s, chain: ", s);
	for (ptr = hptr->omsgidtab; ptr; ptr=ptr->next) {
		if (!strcmp(s, ptr->id)) {
			if (vflag) fprintf(stderr, "omsgid found!\n");
			return(0);
		}
		if (vflag) fprintf(stderr, "%s, ", ptr->id);
	}
	if (vflag) fprintf(stderr, "\n");
	
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->id = strdup(s)))
		error_memory();
	ptr->flags = n;

	/* utilize next pointer: */
	ptr->next = hptr->omsgidtab;
	hptr->omsgidtab = ptr;
	return(0);
}

/* check out-of-order message ID structure */

int
check_omsgid(struct host *hptr, const char *s) {
	struct omsgid *ptr, *prev;
	
	prev = NULL;
	for (ptr = hptr->omsgidtab; ptr; ptr=ptr->next) {
		if (!strcmp(s, ptr->id)) {
			if (vflag) fprintf(stderr, "  %s removed from chain\n", s);
			if (prev == NULL)
				hptr->omsgidtab = ptr->next;
			else
				prev->next = ptr->next;
			free(ptr->id);
			free(ptr);
			return(1);
		}
		prev = ptr;
	}
	return(0);
}

/* update status struct: */

void
update_status(struct host *hptr, const char *s) {
	unsigned hashval;
	struct status *ptr;

	for (ptr = hptr->sttab[hash(s,rsize)];ptr; ptr = ptr->next)
		if (!strcmp(s, ptr->name)) {
			ptr->num++;
			return; 
		}
	hptr->sdif++;
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->name = strdup(s)))
		error_memory();
	ptr->num = 1;
	hashval = hash(s,rsize);
	ptr->next = hptr->sttab[hashval];
	hptr->sttab[hashval] = ptr;
}

/* update ruleset struct: */

void
update_ruleset(struct host *hptr, const char *s, const char *p) {
	unsigned hashval;
	struct rule *ptr;
	struct rrelay *rptr;

	hashval = hash(s,rsize);
	for (ptr = hptr->ruletab[hashval];ptr; ptr = ptr->next)
		if (!strcmp(s, ptr->name)) {
			ptr->num++;

			/* Update relay table: */
			for (rptr = ptr->rrelaytab; rptr; rptr = rptr->next) {
				if (!strcmp(p, rptr->name)) {
					rptr->num++;
					return;
				}
			}
			ptr->reldif++;
			if (!(rptr = malloc(sizeof(*rptr))))
				error_memory();
			if (!(rptr->name = strdup(p)))
				error_memory();
			rptr->num = 1;
			rptr->next = ptr->rrelaytab;
			ptr->rrelaytab = rptr;

			return; 
		}

	/* New entry, set defaults: */
	hptr->rdif++;
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->name = strdup(s)))
		error_memory();
	ptr->rrelaytab = NULL;

	/* Set defaults for first relay */
	if (!(rptr = malloc(sizeof(*rptr))))
		error_memory();
	if (!(rptr->name = strdup(p)))
		error_memory();
	rptr->num = 1;
	rptr->next = ptr->rrelaytab; 
	ptr->rrelaytab = rptr; 

	ptr->num = 1;
	ptr->reldif = 1;
	ptr->next = hptr->ruletab[hashval];
	hptr->ruletab[hashval] = ptr;
}

/* update envelope pairs: */

void
update_envpair(struct host *hptr, const char *s, const char *p, int ssize) {
	unsigned hashval;
	struct envpair *ptr;
	const char *f;
	const char *t;
	char *tmp;
	char *tmp1;

	f = s; t = p;

	if (!(tmp = malloc(strlen(s)+strlen(p))))
		error_memory();
	tmp1 = tmp;
	while(*s != '\0') *tmp++ = *s++;
	while(*p != '\0') *tmp++ = *p++;
	hashval = hash(t,asize);

	for (ptr = hptr->etab[hashval];ptr; ptr = ptr->next)
		if (!strcmp(f, ptr->fname) && !(strcmp(t, ptr->tname))) {
			ptr->num++;
			ptr->size += ssize;
			free(tmp1);
			return; 
		}
	hptr->edif++;
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->fname = strdup(f)))
		error_memory();
	if (!(ptr->tname = strdup(t)))
		error_memory();
	ptr->num = 1;
	ptr->size = ssize;
	ptr->next = hptr->etab[hashval];
	hptr->etab[hashval] = ptr;
	free(tmp1);
}

/* update relay pairs: */

void
update_relpair(struct host *hptr, const char *s, const char *p, int ssize) {
	unsigned hashval;
	struct relpair *ptr;
	const char *f;
	const char *t;
	char *tmp;
	char *tmp1;

	f = s; t = p;

	if (!(tmp = malloc(strlen(s)+strlen(p))))
		error_memory();
	tmp1 = tmp;
	while(*s != '\0') *tmp++ = *s++;
	while(*p != '\0') *tmp++ = *p++;
	hashval = hash(t,rsize);

	for (ptr = hptr->rtab[hashval];ptr; ptr = ptr->next)
		if (!strcmp(f, ptr->fname) && !(strcmp(t, ptr->tname))) {
			ptr->num++;
			ptr->size += ssize;
			free(tmp1);
			return; 
		}
	hptr->rrdif++;
	if (!(ptr = malloc(sizeof(*ptr))))
		error_memory();
	if (!(ptr->fname = strdup(f)))
		error_memory();
	if (!(ptr->tname = strdup(t)))
		error_memory();
	ptr->num = 1;
	ptr->size = ssize;
	ptr->next = hptr->rtab[hashval];
	hptr->rtab[hashval] = ptr;
	free(tmp1);
}
