/************************************************************
** $Id: edit.c,v 0.8 2003/12/15 15:03:33 bcx Exp bcx $
**
** Copyright Bryan Costales and Perforce Software, Inc. 2003
**
** This code is "open source" as defined by version 1.9 of
** the Open Source Definition from:
**
**	http://www.opensource.org/docs/definition.php.
**
** The is the slowedit interface for the milter.
**
*************************************************************/

# define EXTERN extern
# include "slow.h"

extern PRIV *Priv;		/* in test.c */
extern struct smfi_str Ctx;	/* in test.c */

typedef struct {
	char *token;
	int   mode;
} EDITCOMMANDS;

EDITCOMMANDS EditCommands[] = {
# define EDITCOM_ADD		1
	{"add", 	EDITCOM_ADD},		/* add an IP event */
# define EDITCOM_DELETE		2
	{"delete", 	EDITCOM_DELETE},	/* delete an IP and all its events */
# define EDITCOM_LIST		3
	{"list", 	EDITCOM_LIST},		/* list all or selected IP addresses */
# define EDITCOM_FIND		4
	{"find", 	EDITCOM_FIND},		/* list all or selected IP addresses */
# define EDITCOM_ALIAS		5
	{"alias", 	EDITCOM_ALIAS},		/* add an IP alias */
# define EDITCOM_WHITE		6
	{"whitelist", 	EDITCOM_WHITE},		/* whitelist an IP address */
# define EDITCOM_TRIM		7
	{"trim", 	EDITCOM_TRIM},		/* remove an event from an IP */
	{NULL,		0},
};

bool
slowedit(int opt, int argc, char **argv)
{
	int ret, mode;
	EDITCOMMANDS *e;
	char *user;
	extern char *optarg;

	/*
	 * Format of an edit command is "slowedit command switches IP
	 * On entry to this routine, argv[0] is the command.
	 */
	mode = 0;
	for (e = EditCommands; e->token != NULL; ++e)
	{
		if (strcasecmp(argv[opt], e->token) == 0)
		{
			mode = e->mode;
			break;
		}
	}
	if (mode == 0)
	{
		printf("Usage: slowedit ");
		for (e = EditCommands; e->token != NULL; ++e)
			printf("%s%s",
				e == EditCommands ? "" : "|",
				e->token);
		printf("\n");
		return FALSE;
	}

	ret = 0;
	user = cuserid(NULL);
	switch(mode)
	{
	    /* add IP event */
	    case EDITCOM_ADD:
		ret = slowadd(user, opt, argc, argv);
		break;

	    case EDITCOM_DELETE:
		ret = slowdelete(user, opt, argc, argv);
		break;

	    case EDITCOM_LIST:
		ret = slowlist(user, opt, argc, argv);
		break;

	    case EDITCOM_FIND:
		ret = slowfind(user, opt, argc, argv);
		break;

	    case EDITCOM_ALIAS:
		ret = slowalias(user, opt, argc, argv);
		break;

	    case EDITCOM_WHITE:
		ret = slowwhite(user, opt, argc, argv);
		break;

	    case EDITCOM_TRIM:
		ret = slowtrim(user, opt, argc, argv);
		break;
	}
	return ret;
}

bool
slowadd(char *user, int opt, int argc, char **argv)
{
	char *iptext;
	char *event_str;
	char logprefix[BUFSIZ];

	test_init(TRUE);
	++opt;
	if ((argc - opt) < 2)
	{
		(void) printf("Usage: slowedit add IPnumber \"event,event,...\"\n");
		show_events("Combine events from");
		return FALSE;
	}
	iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE);
	++opt;
	event_str = try_strdup(__FILE__, __LINE__, argv[opt], FALSE);
	if (slowsetpriv(iptext, event_str) == FALSE)
		return FALSE;

	if (user == NULL)
		sprintf(logprefix, "edit add: ");
	else
		sprintf(logprefix, "%s added: ", user);
	LogPrefix = logprefix;


	Priv->ip = inet_network(Priv->iptxt);
	if (push_to_db(&Ctx, Priv, FALSE) != SMFIS_CONTINUE)
		return FALSE;

	if (! isdebug(BUG_DELETE))
	{
		(void) printf("slowedit: add %s by %s succeeded\n",
			iptext,
			user == NULL ? "noody" : user);
	}

	(void) free(event_str);
	return TRUE;
}

bool
slowdelete(char *user, int opt, int argc, char **argv)
{
	time_t t;
	char *iptext;
	PRIMARY_KEY ip;
	int ret;

	test_init(TRUE);

	++opt;
	if ((argc - opt) < 1)
	{
		(void) printf("Usage: slowedit delete IPnumber [date]\n");
		(void) printf("\tIf date missing, remove the IP record\n");
		(void) printf("\telse remove just that event record.\n");
		return FALSE;
	}
	iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE);
	ip = inet_network(iptext);
	if ((int)ip == -1)
	{
		(void) printf("slowedit delete: %s: cannot interpret\n",
			iptext);
		return FALSE;
	}

	++opt;
	if (argv[opt] == NULL)
		t = 0;
	else
		t = parse_date(argv[opt]);

	ret = del_ip_database(Dp1, Dp2, ip, t, FALSE);
	if (ret != 0)
	{
		(void) printf("slowedit: delete %s\n",
			ret == DB_NOTFOUND ? "Entry specified not in datbase":
			strerror(errno));
		return FALSE;
	}

# if HAVE_SYSLOG
	if (DoLog == TRUE && LogEvents == TRUE)
		syslog(LOG_INFO, "%s deleted %s %s\n",
			user == NULL ? Prog : user,
			iptext,
			t == 0 ? "" : argv[opt]);
# endif
	(void) printf("slowedit: %s successfully deleted %s, event %s\n",
		user == NULL ? Prog : user, iptext,
		t == 0 ? "all-events" : argv[opt]);

	return TRUE;
}

bool
slowlist(char *user, int opt, int argc, char **argv)
{
	char *iptext;
	PRIMARY_KEY ip;
	PRIMARY_DATUM *dates, *d;
	SECONDARY_DATUM *event;
	SECONDARY_KEY k;
	int ndates;
	int i;

	test_init(TRUE);

	++opt;
	if (argc <= opt)
	{
		printf("Usage: slowedit list [IPnumber | \"all\"]\n");
		return FALSE;
	}
	iptext = try_strdup(__FILE__, __LINE__, argv[opt], FALSE);

	if (strcasecmp(iptext, "all") == 0)
	{
		if (dump_database(Dp1, Dp2, stdout) != 0)
		{
			milterr(Prog, __FILE__, __LINE__, FALSE,
				errno, iptext, "list all failed");
		}
		return TRUE;
	}

	ip = inet_network(iptext);
	if ((int)ip == -1)
	{
		(void) printf("slowedit list: %s: cannot interpret\n",
			iptext);
		return FALSE;
	}

	ndates = 0;
	dates = get_ip_database(Dp1, ip, &ndates, FALSE);
	if (dates == NULL || ndates == 0)
	{
		(void) printf("slowedit: list %s %s\n",
			iptext, ndates == 0 ? "Entry specified not in datbase":
			strerror(errno));
		return FALSE;
	}
	for (d = dates, i = 0; i < ndates; ++i, ++d)
	{
		k.ip = ip;
		k.date = *d;
		event = get_event_database(Dp2, &k, FALSE);
		if (event == NULL)
		{
			if (errno == DB_NOTFOUND)
			{
				printf("slowedit: list %s: no record found for date %s",
					iptext, ctime(d));
				continue;
			}
			printf("slowedit: list %s: lookup error %d\n",
				iptext, errno);
			return FALSE;
		}
		(void)printf("%s\n", event_to_str(iptext, *d, event, FALSE));
	}

	return TRUE;
}

bool
slowfind(char *user, int opt, int argc, char **argv)
{
	LOGICSTRUCT *logic;
	DBT key, data;
	SECONDARY_DATUM *event;
	SECONDARY_KEY k;
	DBC *curs;
	int ret;
	struct in_addr in;
	time_t *tp;
	char ipbuf[MAXHOSTNAMELEN];
	char *cp;
	int i, records;
	BITMAP set;

	test_init(TRUE);
	++opt;
	if (argc == opt)
	{
		(void) printf("Usage: slowedit find \"[and|or],relation,relation,...\"\n");
		return FALSE;
	}
	cp = try_strdup(__FILE__, __LINE__, argv[opt], FALSE);
	logic = slowlogicparse(cp);
	if (logic == NULL)
		return FALSE;

	ret = Dp1->cursor(Dp1, NULL, &curs, 0);
	if (ret != 0)
	{
		Dp1->err(Dp1, ret, "%s\n", PrimaryDatabaseLoc);
		return ret;
	}
	(void) memset(&data, 0, sizeof data);
	(void) memset(&key, 0, sizeof key);
	key.flags = DB_DBT_REALLOC;
	data.flags = DB_DBT_REALLOC;
	for (;;)
	{
		ret = curs->c_get(curs, &key, &data, DB_NEXT);
		if (ret == DB_NOTFOUND)
			break;
		if (ret != 0)
		{
			Dp1->err(Dp1, ret, "%s\n", PrimaryDatabaseLoc);
			return ret;
		}
		if (key.data == NULL)
		{
			continue;
		}
		records = data.size / sizeof(PRIMARY_DATUM);
		(void) memcpy(&in.s_addr, ((char *)(key.data)), sizeof(in.s_addr));
		in.s_addr = htonl(in.s_addr);
		(void) sprintf(ipbuf, "%s", inet_ntoa(in));
		tp = (time_t *)data.data;
		if (records != 0 && *tp < MAX_TYPE_RECORD)
		{
			/* WHITELIST ALIAS */
		}
		for (i = 0; i < records; i++, tp++)
		{
			k.ip = *((PRIMARY_KEY *)(key.data));
			k.date = *tp;
			event = get_event_database(Dp2, &k, FALSE);
			if (event == NULL)
				continue;
			if (logic->andor == SLOWBLOGICAND)
			{
				/*
				 * Since logic AND means everything must match
				 * we continue (skip printing) on any mismatch.
				 */
				switch(logic->envrcpts.relation)
				{
					case SLOWLOGICEQUAL:
						if (event->envrcpts != logic->envrcpts.value)
							continue;
						break;
					case SLOWLOGICGREATER:
						if (event->envrcpts <= logic->envrcpts.value)
							continue;
						break;
					case SLOWLOGICLESSTHAN:
						if (event->envrcpts >= logic->envrcpts.value)
							continue;
						break;
					case SLOWLOGICNONE:
						break;
					default:
						continue;
				}
				switch(logic->hdrrcpts.relation)
				{
					case SLOWLOGICEQUAL:
						if (event->headrcpts != logic->hdrrcpts.value)
							continue;
						break;
					case SLOWLOGICGREATER:
						if (event->headrcpts <= logic->hdrrcpts.value)
							continue;
						break;
					case SLOWLOGICLESSTHAN:
						if (event->headrcpts >= logic->hdrrcpts.value)
							continue;
						break;
					case SLOWLOGICNONE:
						break;
					default:
						continue;
				}
				switch(logic->honeyrcpts.relation)
				{
					case SLOWLOGICEQUAL:
						if (event->honeyrcpts != logic->honeyrcpts.value)
							continue;
						break;
					case SLOWLOGICGREATER:
						if (event->honeyrcpts <= logic->honeyrcpts.value)
							continue;
						break;
					case SLOWLOGICLESSTHAN:
						if (event->honeyrcpts >= logic->honeyrcpts.value)
							continue;
						break;
					case SLOWLOGICNONE:
						break;
					default:
						continue;
				}
				switch(logic->bad.relation)
				{
					case SLOWLOGICEQUAL:
						if (event->badrcpts != logic->bad.value)
							continue;
						break;
					case SLOWLOGICGREATER:
						if (event->badrcpts <= logic->bad.value)
							continue;
						break;
					case SLOWLOGICLESSTHAN:
						if (event->badrcpts >= logic->bad.value)
							continue;
						break;
					case SLOWLOGICNONE:
						break;
					default:
						continue;
				}
				if (logic->bits[SLOWSETBITS] != 0)
				{
					/*
					 * The specified bits must be set 
					 */
					set = logic->bits[SLOWSETBITS] & event->eventmap;
					if (set == 0)
						continue;
				}
				if (logic->bits[SLOWONLYBITS] != 0)
				{
					/*
					 * The specified bits must be the only ones set 
					 */
					set = logic->bits[SLOWONLYBITS] - event->eventmap;
					if (set != 0)
						continue;
				}
				if (logic->bits[SLOWCLEARBITS] != 0)
				{
					/*
					 * The specified bits must be clear (0)
					 */
					set = logic->bits[SLOWCLEARBITS] & ~(event->eventmap);
					if (set == 0)
						continue;
				}
				if (logic->reg != NULL && event->msgid[0] != '\0')
				{
# if HAVE_REGEX_H && HAVE_REGCOMP
					regmatch_t pmatch;
					int rerr;

					rerr = regexec(logic->reg, event->msgid, 1, &pmatch, 0);
					if (rerr != 0)
						continue;
# else
					if (strstr(event->msgid, logic->reg) != 0)
						continue;
# endif
				}
				else if (logic->reg != NULL)
					continue;

				(void) printf("%s\n", event_to_str(ipbuf, k.date, event, FALSE));
				continue;
			}
			/*
			 * Since logic OR means anythin may match
			 * jump to print on any match.
			 */
			switch(logic->envrcpts.relation)
			{
				case SLOWLOGICEQUAL:
					if (event->envrcpts == logic->envrcpts.value)
						goto print;
					break;
				case SLOWLOGICGREATER:
					if (event->envrcpts > logic->envrcpts.value)
						goto print;
					break;
				case SLOWLOGICLESSTHAN:
					if (event->envrcpts < logic->envrcpts.value)
						goto print;
					break;
			}
			switch(logic->hdrrcpts.relation)
			{
				case SLOWLOGICEQUAL:
					if (event->headrcpts == logic->hdrrcpts.value)
						goto print;
					break;
				case SLOWLOGICGREATER:
					if (event->headrcpts > logic->hdrrcpts.value)
						goto print;
					break;
				case SLOWLOGICLESSTHAN:
					if (event->headrcpts < logic->hdrrcpts.value)
						goto print;
					break;
			}
			switch(logic->honeyrcpts.relation)
			{
				case SLOWLOGICEQUAL:
					if (event->honeyrcpts == logic->honeyrcpts.value)
						goto print;
					break;
				case SLOWLOGICGREATER:
					if (event->honeyrcpts > logic->honeyrcpts.value)
						goto print;
					break;
				case SLOWLOGICLESSTHAN:
					if (event->honeyrcpts < logic->honeyrcpts.value)
						goto print;
					break;
			}
			switch(logic->bad.relation)
			{
				case SLOWLOGICEQUAL:
					if (event->badrcpts == logic->bad.value)
						goto print;
					break;
				case SLOWLOGICGREATER:
					if (event->badrcpts > logic->bad.value)
						goto print;
					break;
				case SLOWLOGICLESSTHAN:
					if (event->badrcpts < logic->bad.value)
						goto print;
					break;
			}
			if (logic->bits[SLOWSETBITS] != 0)
			{
				/*
				 * The specified bits must be set 
				 */
				set = logic->bits[SLOWSETBITS] & event->eventmap;
				if (set != 0)
					goto print;
			}
			if (logic->bits[SLOWONLYBITS] != 0)
			{
				/*
				 * The specified bits must be the only ones set 
				 */
				set = logic->bits[SLOWONLYBITS] - event->eventmap;
				if (set == 0)
					goto print;
			}
			if (logic->bits[SLOWCLEARBITS] != 0)
			{
				/*
				 * The specified bits must be clear (0)
				 */
				set = logic->bits[SLOWCLEARBITS] & ~(event->eventmap);
				if (set != 0)
					goto print;
			}
			if (logic->reg != NULL && event->msgid[0] != '\0')
			{
# if HAVE_REGEX_H && HAVE_REGCOMP
				regmatch_t pmatch;

				if (regexec(logic->reg, event->msgid, 1, &pmatch, 0) == 0)
# else
				if (strstr(event->msgid, logic->reg) == 0)
# endif
					goto print;
				
			}
			continue;
print:
			(void) printf("%s\n", event_to_str(ipbuf, k.date, event, FALSE));
			continue;
		}
	}
	(void) curs->c_close(curs);
	return TRUE;
}

bool
slowalias(char *user, int opt, int argc, char **argv)
{
	test_init(TRUE);
	printf("Usage: slowedit alias IPexisting IPnew\n");
	return TRUE;
}

bool
slowwhite(char *user, int opt, int argc, char **argv)
{
	test_init(TRUE);
	printf("Usage: slowedit white IPnumber why\n");
	return TRUE;
}

bool
slowtrim(char *user, int opt, int argc, char **argv)
{
	test_init(TRUE);
	printf("Usage: slowedit trim [-o oldest] [-n newest] [-r range] \n");
	return TRUE;
}

bool
slowgarbage(char *user, int opt, int argc, char **argv)
{
	test_init(TRUE);
	printf("Usage: slowedit garbage\n");
	return TRUE;
}

bool
slowrebuild(char *user, int opt, int argc, char **argv)
{
	test_init(TRUE);
	printf("Usage: slowedit rebuild [-o outputfile] [-i inputfile]\n");
	return TRUE;
}

typedef struct {
	char *event;
	int   index;
} EVENT_LIST;

EVENT_LIST EventList[] = {
# define EVENT_ENVRCPTS		1
	{"er", EVENT_ENVRCPTS},	/* envelope recipients */
# define EVENT_HDRRCPTS		2
	{"hr", EVENT_HDRRCPTS},	/* header recipients */
# define EVENT_HONEYRCPTS	3
	{"ho", EVENT_HONEYRCPTS},	/* honey pot recipients */
# define EVENT_BADRCPTS		4
	{"br", EVENT_BADRCPTS},	/* bad envelope recipients */
# define EVENT_TOOHDR		5
	{"hed", EVENT_TOOHDR},	/* too many header recipients */
# define EVENT_TOOENV		6
	{"env", EVENT_TOOENV},	/* too many envelope recipients */
# define EVENT_HONEY		7
	{"hon", EVENT_HONEY},	/* one or more honey pots rcpts */
# define EVENT_MSGID		8
	{"mid", EVENT_MSGID},	/* bad or missing message-id header */
# define EVENT_FROMH		9
	{"frm", EVENT_FROMH},	/* bad from header */
# define EVENT_BADHO		10
	{"bho", EVENT_BADHO},	/* bad envelope sender host */
# define EVENT_RBL		11
	{"rbl", EVENT_RBL},	/* reported bad by an RBL host */
# define EVENT_MILT		12
	{"abo", EVENT_MILT},	/* The milter aborted */
# define EVENT_ADVW		13
	{"pip", EVENT_ADVW},	/* Attempt at an advanced write */
# define EVENT_FORGE		14
	{"for", EVENT_FORGE},	/* sendmail reported this as forged */
# define EVENT_FROMMX		15
	{"mmx", EVENT_FROMMX},	/* received from one of our MX servers */
# define EVENT_NOTINET		16
	{"net", EVENT_NOTINET},	/* not received via a TCP/IP network socket */
# define EVENT_MESGID		17
	{"msgid", EVENT_MESGID},	/* The text that is the message-id header */
	{NULL,	0},
};

void
show_events(char *str)
{
	EVENT_LIST *e;
	int count;

	printf("%s:\n\t", str);
	count = 0;
	for (e = EventList; e->event != NULL; ++e)
	{
		if (++count == 6)
		{
			printf("\n\t");
			count = 0;
		}
		else if (e != EventList)
			printf(", ");
		if (strlen(e->event) == 2)
			printf("%s=#", e->event);
		else
			printf("%s", e->event);
	}
	printf("\n");
}
int
is_event(char *str)
{
	EVENT_LIST *e;

	for (e = EventList; e->event != NULL; ++e)
	{
		if (strcasecmp(e->event, str) == 0)
			return e->index;
	}
	return 0;
}

bool
slowsetpriv(char *iptext, char *estr)
{
	char **list, **l;
	char **equate;
	char *item;
	int   value;
	int   index;

	list = mkargv(estr, ',', FALSE);
	equate = NULL;
	if (list == NULL)
	{
		(void) printf("slowedit: events missing\n");
		return FALSE;
	}
	if (isdebug(BUG_EDIT))
		print_list("slowsetpriv", list, stdout);

	for (l = list; *l != NULL; l++)
	{
		value = -1;
		if (strchr(*l, '=') != NULL)
		{
			equate = mkargv(*l, '=', FALSE);
			if (equate == NULL)
				continue;
			item = try_strdup(__FILE__, __LINE__, equate[0], FALSE);
			if (equate[1] != NULL)
				value = atoi(equate[1]);
			else
				value = 0;
		}
		else
			item = try_strdup(__FILE__, __LINE__, *l, FALSE);

		index = is_event(item);
		if (index == 0)
		{
			(void) printf("%s: Unrecognized event keyword\n", item);
			show_events("Combine events from");
			return FALSE;
		}

		if (strlen(item) == 3 && value >= 0)
		{
			(void) printf("slowedit: %s: Three letter events are boolean.", item);
			(void) printf(" Do not use %s=value form.\n", item);
			return FALSE;
		}

		switch(index)
		{
		    /* count of envelope recipients */
		    case EVENT_ENVRCPTS:
			if (value <= 0)
			{
				milterr(Prog, __FILE__, __LINE__, FALSE,
					EINVAL, item,
					"May not have a value zero or less");
			}
			Priv->envrcpts = value;
			break;

		    case EVENT_HDRRCPTS:
			if (value <= 0)
			{
				milterr(Prog, __FILE__, __LINE__, FALSE,
					EINVAL, item,
					"May not have a value zero or less");
			}
			Priv->nhdrrcpts = value;
			break;

		    case EVENT_HONEYRCPTS:
			if (value <= 0)
			{
				milterr(Prog, __FILE__, __LINE__, FALSE,
					EINVAL, item,
					"May not have a value zero or less");
			}
			Priv->honeyrcpts = value;
			break;

		    case EVENT_BADRCPTS:
			if (value <= 0)
			{
				milterr(Prog, __FILE__, __LINE__, FALSE,
					EINVAL, item,
					"May not have a value zero or less");
			}
			Priv->badrcpts = value;
			break;

		    case EVENT_TOOHDR:
			set_bit(BIT_TOO_MANY_HDR_RCPTS, Priv->bits);
			break;

		    case EVENT_TOOENV:
			set_bit(BIT_TOO_MANY_ENV_RCPTS, Priv->bits);
			break;

		    case EVENT_HONEY:
			set_bit(BIT_GOT_HONEYPOT, Priv->bits);
			break;

		    case EVENT_MSGID:
			set_bit(BIT_BAD_MSG_ID, Priv->bits);
			break;

		    case EVENT_FROMH:
			set_bit(BIT_BAD_FROM_HEAD, Priv->bits);
			break;

		    case EVENT_BADHO:
			set_bit(BIT_BAD_HOST, Priv->bits);
			break;

		    case EVENT_RBL:
			set_bit(BIT_RBL_BAD, Priv->bits);
			break;

		    case EVENT_MILT:
			set_bit(BIT_MILTER_ABORTED, Priv->bits);
			break;

		    case EVENT_ADVW:
			set_bit(BIT_ADVANCEWRITE, Priv->bits);
			break;

		    case EVENT_FORGE:
			set_bit(BIT_FORGED, Priv->bits);
			break;

		    case EVENT_FROMMX:
			set_bit(BIT_FROMMX, Priv->bits);
			break;

		    case EVENT_NOTINET:
			set_bit(BIT_NOTINET, Priv->bits);
			break;
		    
		    case EVENT_MESGID:
			if (equate != NULL && equate[1] != NULL)
				Priv->midstr = try_strdup(__FILE__, __LINE__, equate[1], FALSE);
			break;
		}
		(void) free(item);
	}
	Priv->iptxt = try_strdup(__FILE__, __LINE__, iptext, FALSE);
	return TRUE;
}

LOGICSTRUCT *
slowlogicparse(char *estr)
{
	char **list, **l;
	char **equate;
	char *item;
	int   val;
	int   index;
	int   ch, rel, whichbits;
	LOGICSTRUCT *logic;

	list = mkargv(estr, ',', FALSE);
	if (list == NULL)
	{
		(void) printf("slowedit: events missing\n");
		return FALSE;
	}
	if (isdebug(BUG_EDIT))
		print_list("slowsetpriv", list, stdout);

	logic = (LOGICSTRUCT *)try_alloc(__FILE__, __LINE__, sizeof(*logic), FALSE);
	if (logic == NULL)
		return NULL;
	(void)memset((char *)logic, '\0', sizeof(*logic));

	logic->andor = SLOWBLOGICAND; /* default to AND */

	for (l = list; *l != NULL; l++)
	{
		if (strcasecmp(*l, "and") == 0)
		{
			logic->andor = SLOWBLOGICAND;
			continue;
		}
		if (strcasecmp(*l, "or") == 0)
		{
			logic->andor = SLOWBLOGICOR;
			continue;
		}
			
		val = -1;
		rel = SLOWLOGICNONE;
		whichbits = SLOWSETBITS;

		if (strchr(*l, '=') != NULL)
		{
			rel = SLOWLOGICEQUAL;
			ch = '=';
		}
		else if (strchr(*l, '>') != NULL)
		{
			rel = SLOWLOGICGREATER;
			ch = '>';
		}
		else if (strchr(*l, '<') != NULL)
		{
			rel = SLOWLOGICLESSTHAN;
			ch = '<';
		}
		else
		{
			(void) printf("%s: Unrecognized or missing comparitor\n", *l);
			(void) printf("\tselect from: = (equal to), < (less than), or > (greater than\n");
			exit (EINVAL);
		}
	
		/*
		 * Other than "and" and "or", everyting is in the format
		 *     keyword [<>=] value,
		 * For non-bitmap items, value is numeric.
		 * For bitmap items, value must be =[set,only,clear]
		 * For msgid value must be ="regular expression"
		 */
		equate = mkargv(*l, ch, FALSE);
		if (equate == NULL)
			continue;
		if (equate[1] == NULL)
		{
			(void) printf("%s: Value portion missing from relationshipe.\n", *l);
			exit(EINVAL);
		}
		item = try_strdup(__FILE__, __LINE__, equate[0], FALSE);
		index = is_event(item);
		if (index == 0)
		{
			(void) printf("%s: Unrecognized event keyword\n", item);
			show_events("Combine events from");
			return FALSE;
		}

		if (isdigit((int)(equate[1][0])))
		{
			val = atoi(equate[1]);
			whichbits = SLOWSETBITS;
		}
		else
		{
			if (index == EVENT_MESGID)
			{
# if HAVE_REGEX_H && HAVE_REGCOMP
				int rerr;

				logic->reg = try_alloc(__FILE__, __LINE__, sizeof(regex_t), FALSE);
				memset((char *)(logic->reg), '\0', sizeof(regex_t));
				rerr = regcomp(logic->reg, equate[1], REG_EXTENDED|REG_ICASE);
				if (rerr != 0)
				{
					char rerrbuf[BUFSIZ];

					(void) regerror(rerr, logic->reg, rerrbuf, BUFSIZ);
					(void) printf("%s: %s\n", item, rerrbuf);
					exit (rerr);
				}

# else
				logic->reg = try_strdup(__FILE__, __LINE__, equate[1], FALSE);
# endif
				continue;
			}
			val = 0;
			if (strcasecmp(equate[1], "set") == 0)
				whichbits = SLOWSETBITS;
			else if (strcasecmp(equate[1], "only") == 0)
				whichbits = SLOWONLYBITS;
			else if (strcasecmp(equate[1], "clear") == 0)
				whichbits = SLOWCLEARBITS;
			else
			{
				(void) printf("%s: Unrecognized bit keyword\n", item);
				(void) printf("\tselect from: set, only, or clear\n");
				exit (EINVAL);
			}
		}

		switch(index)
		{
		    case EVENT_ENVRCPTS:
			logic->envrcpts.relation = rel;
			logic->envrcpts.value = val;
			break;
		    case EVENT_HDRRCPTS:
			logic->hdrrcpts.relation = rel;
			logic->hdrrcpts.value = val;
			break;
		    case EVENT_HONEYRCPTS:
			logic->honeyrcpts.relation = rel;
			logic->honeyrcpts.value = val;
			break;
		    case EVENT_BADRCPTS:
			logic->bad.relation = rel;
			logic->bad.value = val;
			break;

		    case EVENT_TOOHDR:
			set_bit(BIT_TOO_MANY_HDR_RCPTS, logic->bits[whichbits]);
			break;

		    case EVENT_TOOENV:
			set_bit(BIT_TOO_MANY_ENV_RCPTS, logic->bits[whichbits]);
			break;

		    case EVENT_HONEY:
			set_bit(BIT_GOT_HONEYPOT, logic->bits[whichbits]);
			break;

		    case EVENT_MSGID:
			set_bit(BIT_BAD_MSG_ID, logic->bits[whichbits]);
			break;

		    case EVENT_FROMH:
			set_bit(BIT_BAD_FROM_HEAD, logic->bits[whichbits]);
			break;

		    case EVENT_BADHO:
			set_bit(BIT_BAD_HOST, logic->bits[whichbits]);
			break;

		    case EVENT_RBL:
			set_bit(BIT_RBL_BAD, logic->bits[whichbits]);
			break;

		    case EVENT_MILT:
			set_bit(BIT_MILTER_ABORTED, logic->bits[whichbits]);
			break;

		    case EVENT_ADVW:
			set_bit(BIT_ADVANCEWRITE, logic->bits[whichbits]);
			break;

		    case EVENT_FORGE:
			set_bit(BIT_FORGED, logic->bits[whichbits]);
			break;

		    case EVENT_FROMMX:
			set_bit(BIT_FROMMX, logic->bits[whichbits]);
			break;

		    case EVENT_NOTINET:
			set_bit(BIT_NOTINET, logic->bits[whichbits]);
			break;

		}
		(void) free(item);
	}
	return logic;
}

