/*
 * EnderUNIX Software Development Team, (c) 2002
 * Istanbul / Turkiye
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#include <sys/types.h>

#include "loadconfig.h"
#include "functions.h"
#include "wildmat.h"

extern char mail_command[VALSIZE];
extern char makemap_command[VALSIZE];
extern char sysadmin[VALSIZE];
extern char statfile[VALSIZE];
extern char badmailfile[VALSIZE];


extern int wcnt;
extern int bcnt;
extern int pcnt;


extern int w;
extern int b;
extern int p;

/* check if the supplied mail address exist in our "ignored" linked list */
int isIgnored(char *email)
{
	iaddr *l = iaddrlist;

	for ( ; l !=  NULL; l = l->next)  {
		if (DoMatch(email, l->mail) == TRUE) 
			return 1;
	}
	return -1;
}

/* Check if the email address if already in list.
 * If so, increment its hit count; if not, 
 * add it to end of the list */
void check_addr(char *email)
{
	int h;
	maddr *sym = NULL;

	/* Calculate hash sum, locate and inc. cnt if email exists, 
	 * otherwise place it in spammer_hash */
	h = hash(email);
	for (sym = spammer_hash[h]; sym != NULL; sym = sym->next)
		if (strcmp(email, sym->mail) == 0) {
			sym->cnt++;
			return;
		}

	sym = (maddr *)malloc(sizeof(maddr));
	/* No need a malloc for mail, because, email's been malloc'ed before */
	sym->mail = email;
	sym->cnt = 1;
	sym->next = spammer_hash[h];
	spammer_hash[h] = sym;
	return;
}

void addToIgnored(char *email) 
{

	iaddr *new = NULL;

	new = (iaddr *)malloc(sizeof(iaddr));
	new->mail = email;
	new->next = iaddrlist;
	iaddrlist = new;
}

/* remove some unnecessary characters */
void rmspace(char *s, char *t)
{

	while( *s != '\0') {
		if (*s == ' ' || *s == '\n' || *s == '\r' || *s == '\t')  
			s++;
		else  
			*t++ = *s++;
	}
}

/* notifying system administrator of spammer activity */
int send_notify_mail(char *n)
{

        FILE *inpp;
        char tmpfile[BUFSIZE];
        char mailcmd[BUFSIZE];
        int fd, retval, ret;
        size_t bytes;

        strncpy(tmpfile, "/tmp/qstspam-XXXXXX", BUFSIZE);
        if ((fd = mkstemp(tmpfile)) == -1) {
                puts("Couldn't create temporary file name");
                perror("mkstemp");
         	return errno;
        }
        if ((bytes = write(fd, n, strlen(n))) < bytes) {
                fprintf(stderr, "Couldn't write to temporary file");
                return errno;
        }

        if ((ret = close(fd)) == -1) {
                fprintf(stderr, "Couldn't close temporary file");
                perror("close");
                return errno;
        }
        strcpy(mailcmd, mail_command);
        strcat(mailcmd, " -s  \"spamGuard notification!\"  ");
        strcat(mailcmd, sysadmin);
        strcat(mailcmd, " < ");
        strcat(mailcmd, tmpfile);
        if ((inpp = popen(mailcmd, "r")) == NULL) {
                fprintf(stderr, "Couldn't send mail");
                perror("popen");
                return errno;
        }

	if ((retval = pclose(inpp)) == -1) {
                perror("pclose");
                return errno;
        }
        if ((ret = unlink(tmpfile)) == -1) {
                perror("unlink");
                return errno;
        }
        return retval;
}

unsigned int hash(char *str)
{
	unsigned int h;
	unsigned char *p = NULL;

	h = 0;
	for (p = (unsigned char *)str; *p != '\0'; p++)
		h = MULTIPLIER * h + *p;
	return h % MAXADDR;
}

void save_pos(hist_stat *h)
{
	FILE *fp;

	if ((fp = fopen(statfile, "w")) == NULL) {
		fprintf(stderr, "Couldn't open %s: %s\n", statfile, strerror(errno));
		return;
	}

	fprintf(fp, "%d %d", h->inode, h->saved_pos);
	if ((fclose(fp)) == -1) {
		fprintf(stderr, "Couldn't close %s: %s\n", statfile, strerror(errno));
		return;
	}
}

void get_saved_pos(hist_stat *h)
{
	FILE *fp;
	char inode[16];
	char pos[16];

	if ((fp = fopen(statfile, "r")) == NULL) {
		fprintf(stderr, "Couldn't open %s: %s\n", statfile, strerror(errno));
		return;
	}

	fscanf(fp, "%s %s", inode, pos);

	if (inode != NULL)
		h->inode = atoi(inode);
	if (pos != NULL)
		h->saved_pos = atoi(pos);

	if ((fclose(fp)) == -1) {
		fprintf(stderr, "Couldn't close %s: %s\n", statfile, strerror(errno));
		return;
	}
}

int makemap(void)
{
	FILE *fp;
	int retval;

	if ((fp = popen(makemap_command, "r")) == NULL) {
		fprintf(stderr, "Couldn't create hash database file\n");
		perror("popen");
	}

	if ((retval = pclose(fp)) == -1) {
		perror("pclose");
		return errno;
	}
	return retval;
}

/* loads ignore user list from spam-ignore.txt */
void loadIgnoreList(char *fn) 
{

	FILE *fp;
	char buf[1024];
	char *t;
	t = (char *) calloc(1024, sizeof(char));
	if ((fp = fopen(fn, "r")) == NULL) {
		fprintf(stderr, "fopen: %s: %s\n", fn, strerror(errno));
		exit(-1);
	}
	memset(buf, 0, 1024);
	memset(t, 0, 1024);
	while ((fgets(buf, 1024, fp)) != NULL) {
		rmspace(buf, t);
		addToIgnored(strdup(t));
		memset(t, 0, 1024);
		memset(buf, 0, 1024);
	}
	free(t);
	fclose(fp);
}

/* fills our ignore list. these addresses will not be treated as spammers */
void loadIgnore_sendmail(char *fn) 
{

	FILE *fp;
	char buf[1024];
	char *m;
	char *t;

	if ((fp = fopen(fn, "r")) == NULL) {
		fprintf(stderr, "fopen: %s: %s\n", fn, strerror(errno));
		exit(-1);
	}

	memset(buf, 0, 1024);
	while ((fgets(buf, 1024, fp)) != NULL) {
		if (buf[0] == '#' || buf[0] == ' ')
			continue;
		m = strtok(buf, "\t");
		t = (char *)calloc(strlen(m), sizeof(char));
		rmspace(m, t);
		addToIgnored(t);
		memset(buf, 0, 1024);
	}
	fclose(fp);
}

/* This function reads the logfile and fills the " from " linked list */
/* traverse "from" linked list and decide whether the hit count per mail address
 * exceeds the "spammer threshold" */

int qmail_finalize(void)
{

	FILE *fp;
	maddr *ptr;
	char mailbuf[2048];
	int spamcnt = 0;
	int i;


	if ((fp = fopen(badmailfile, "a+")) == NULL) {
		fprintf(stderr, "fopen: %s: %s\n", badmailfile, strerror(errno));
		exit(-1);
	}

	for (i = 0; i < MAXADDR; i++) 
		for (ptr = spammer_hash[i];  ptr != NULL; ptr = ptr->next) {
		/*	printf ("mail: %s\n", ptr->mail); */
			if ((ptr->cnt < bcnt) && w == 1 && (ptr->cnt >= wcnt) && (isIgnored(ptr->mail) < 0)) {
				printf("Light Spammer:%s  sent %d mails\n", ptr->mail, ptr->cnt);
				sprintf(mailbuf, " %s has been spamming your box! (sent %d mails)\n This mail is to notify you that this email address sent more emails than\n your \"warning threshold\", I'm not adding it to %s\n\n -EnderUNIX spamGuard %s\n http://www.enderunix.org/spamguard\n", ptr->mail, ptr->cnt, badmailfile, VERSION);
				send_notify_mail(mailbuf);
			}
			else 
			if (ptr->cnt > bcnt) {
				if (p == 1 && (ptr->cnt >= pcnt)) {
					spamcnt = 1;
					fprintf(fp, "%s\n", ptr->mail);
					printf("Paranoid Spammer:%s  sent %d mails\n", ptr->mail, ptr->cnt);
					sprintf(mailbuf, " %s has been spamming your box! (sent %d mails)\n No matter this mail address is matched against your ignore list, or not,\n I'm still adding it to blacklist since s/he sent more mails then\n your paranoid threshold.\n\n Source mail address has been added to %s file\n Target successfully nuked!\n\n Regards,\n -EnderUNIX spamGuard %s\n http://www.enderunix.org/spamguard\n", ptr->mail, ptr->cnt,  badmailfile, VERSION);
					send_notify_mail(mailbuf);
				}
				else if ((isIgnored(ptr->mail)) < 0) {
					spamcnt = 1;
					fprintf(fp, "%s\n", ptr->mail);
					printf("Spammer:%s  sent %d mails\n", ptr->mail, ptr->cnt);
					sprintf(mailbuf, " %s has been spamming your box! (sent %d mails)\n Source mail address has been added to %s file\n Target successfully nuked!\n\n Regards,\n -EnderUNIX spamGuard %s\n http://www.enderunix.org/spamguard\n", ptr->mail, ptr->cnt, badmailfile, VERSION);
					send_notify_mail(mailbuf);
				}
			}
		}
	fclose(fp);
	return spamcnt;
}

int sendmail_finalize(void)
{

	FILE *fp;
	maddr *ptr;
	char mailbuf[2048];
	int spamcnt = 0;
	int i;

	if ((fp = fopen(badmailfile, "a+")) == NULL) {
		fprintf(stderr, "fopen: %s: %s\n", badmailfile, strerror(errno));
		exit(-1);
	}

	for (i = 0; i < MAXADDR; i++) 
		for (ptr = spammer_hash[i];  ptr != NULL; ptr = ptr->next) {
			if ((ptr->cnt < bcnt) && w && (ptr->cnt >= wcnt) &&  (isIgnored(ptr->mail) < 0)) {
				printf("Light Spammer:%s  sent %d mails\n", ptr->mail, ptr->cnt);
				sprintf(mailbuf, " %s has been spamming your box! (sent %d mails)\n This mail is to notify you that this email address sent more emails than\n your \"warning threshold\", I'm not adding it to %s\n\n -EnderUNIX spamGuard %s\n http://www.enderunix.org/spamguard\n", ptr->mail, ptr->cnt, badmailfile, VERSION);
				send_notify_mail(mailbuf);
			}
			else 
			if (ptr->cnt > bcnt) {
				if (p && (ptr->cnt >= pcnt)) {
					spamcnt = 1;
					fprintf(fp, "%s\tERROR:\"550: Your address is blocked because of spammer activity [http://www.enderunix.org/spamguard]\"\n", ptr->mail);
					printf("Paranoid Spammer:%s  sent %d mails\n", ptr->mail, ptr->cnt);
					sprintf(mailbuf, " %s has been spamming your box! (sent %d mails)\n No matter this mail address is matched against your ignore list, or not,\n I'm still adding it to blacklist since s/he sent more mails then\n your paranoid threshold.\n\n Source mail address has been added to %s file\n Target successfully nuked!\n\n Regards,\n -EnderUNIX spamGuard %s\n http://www.enderunix.org/spamguard\n", ptr->mail, ptr->cnt,  badmailfile, VERSION);
					send_notify_mail(mailbuf);
				}
				else if ((isIgnored(ptr->mail)) < 0) {
					spamcnt = 1;
					fprintf(fp, "%s\tERROR:\"550: Your address is blocked because of spammer activity [http://www.enderunix.org/spamguard]\"\n", ptr->mail);
					printf("Spammer:%s  sent %d mails\n", ptr->mail, ptr->cnt);
					sprintf(mailbuf, " %s has been spamming your box! (sent %d mails)\n Source mail address has been added to %s file\n Target successfully nuked!\n\n Regards,\n -EnderUNIX spamGuard %s\n http://www.enderunix.org/spamguard\n", ptr->mail, ptr->cnt, badmailfile, VERSION);
					send_notify_mail(mailbuf);
				}
			}
		}
	fclose(fp);
	return spamcnt;
}

void print_list(int list)
{
	int i;
	maddr *m;
	list = 0;
	
	for (i = 0; i < MAXADDR; i++) 
		if (spammer_hash[i] != NULL) 
			for (m = spammer_hash[i]; m != NULL; m = m->next)
				printf("%s - %d mails\n", m->mail, m->cnt);
}


syntax highlighted by Code2HTML, v. 0.9.1