diff -urw sendmail-8.13.8-orig/sendmail/conf.c sendmail-8.13.8-mr16/sendmail/conf.c
--- sendmail-8.13.8-orig/sendmail/conf.c	2006-03-22 17:49:33.000000000 -0500
+++ sendmail-8.13.8-mr16/sendmail/conf.c	2007-02-06 23:05:13.000000000 -0500
@@ -595,6 +621,12 @@
 		map_parseargs, stab_map_open, null_map_close,
 		stab_map_lookup, stab_map_store);
 
+#if _FFR_CLASS_MAP
+	MAPDEF("class", NULL, MCF_OPTFILE,
+		map_parseargs, null_map_open, null_map_close,
+		class_map_lookup, null_map_store);
+#endif /* _FFR_CLASS_MAP */
+
 	MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE,
 		map_parseargs, impl_map_open, impl_map_close,
 		impl_map_lookup, impl_map_store);
@@ -6352,6 +6397,29 @@
 	/* Turn all errors into temporary errors. */
 	"_FFR_SOFT_BOUNCE",
 #endif /* _FFR_SOFT_BOUNCE */
+#if _FFR_CLASS_MAP
+	/* del_class() and del_class_regex() are available to delete values from classes */
+	/* bufdump_class() and bufdump_class_regex() are available to dump values from classes */
+	/* wordinclass_regex() is available to lookup values in classes */
+	/* Kclass can now be used with these parameters:
+	 *
+	 * -k for class name
+	 *  	if this is not provided, the firt parameter passed to the map needs to be the class name
+	 *
+	 * -v for lookup type can be any of 
+	 *  	L for Lookup
+	 *	D for Delete
+	 *	A for Add
+	 *	U for dUmp
+	 *	R for regex matching to be used in Lookup, Delete and dUmp operations
+	 *
+	 * -z for delim since dUmp will return multiple values
+	 *
+	 * -a append on success
+	 * -T append on failure
+	 */
+	"_FFR_CLASS_MAP",
+#endif /* _FFR_CLASS_MAP */
 #if _FFR_SPT_ALIGN
 	/*
 	**  It looks like the Compaq Tru64 5.1A now aligns argv and envp to 64
diff -urw sendmail-8.13.8-orig/sendmail/map.c sendmail-8.13.8-mr16/sendmail/map.c
--- sendmail-8.13.8-orig/sendmail/map.c	2006-04-17 21:26:41.000000000 -0400
+++ sendmail-8.13.8-mr16/sendmail/map.c	2007-04-08 17:43:43.000000000 -0400
@@ -5737,6 +5737,164 @@
 
 	return true;
 }
+
+#if _FFR_CLASS_MAP
+char *
+class_map_lookup(map, name, av, statp)
+        MAP *map;
+        char *name;
+        char **av;
+        int *statp;
+{
+	char * frval = "";
+	char * srval = "FOUND";
+	int mid;
+	char *classname;
+	char *p;
+	int clm_lookup = 0;
+	int clm_regex = 0;
+	int i = 0;
+	static char * buf = NULL;
+	static size_t bufsz = 0;
+	size_t len = 0;
+	int results = 0;
+	char delim_char[2];
+	char *delim = delim_char;
+
+
+	if (!map || !av || !av[0])
+	{
+		if (statp)
+			*statp = EX_OK - 1;
+		return NULL;
+	}
+
+	p = map->map_valcolnm;
+
+	if (map->map_keycolnm)
+		classname = map->map_keycolnm;
+	else
+		classname = av[i++];
+
+	mid = macid(classname);
+	if (!mid)
+	{
+		if (statp)
+			*statp = EX_OK - 1;
+		return NULL;
+	}
+
+	while (p)
+	{
+		while (isascii(*p) && isspace(*p))
+			p++;
+		if (!*p)
+			break;
+		switch (*p++)
+		{
+			case 'L':
+				clm_lookup = 0;
+				break;
+			case 'D':
+				srval = "DELETED";
+				clm_lookup = 1;
+				break;
+			case 'A':
+				srval = "ADDED";
+				clm_lookup = 2;
+				break;
+			case 'U':
+				clm_lookup = 3;
+				break;
+			case 'R':
+				clm_regex = 1;
+				break;
+			default:
+				break;
+		}
+	}
+
+	if (map->map_app)
+		srval = map->map_app;
+	if (map->map_tapp)
+		frval = map->map_tapp;
+
+	delim_char[0] = map->map_coldelim;
+	delim_char[1] = '\0';
+
+	if (!delim_char[0])
+		delim_char[0] = ':';
+
+	if (statp)
+		*statp = EX_OK;
+
+	switch (clm_lookup)
+	{
+		case 0:
+			if (clm_regex)
+			{
+# if MAP_REGEX
+				for (; av[i]; i++)
+					results += regex_wordinclass(av[i], mid);
+# else /* MAP_REGEX */
+				return NULL;
+# endif /* MAP_REGEX */
+			}
+			else for (; av[i]; i++)
+				results += wordinclass(av[i], mid);
+
+			if (results > EX_OK)
+				return map_rewrite(map, srval, strlen(srval), NULL);
+			else
+				return map_rewrite(map, frval, strlen(frval), NULL);
+			break;
+
+		case 1:
+			if (clm_regex)
+			{
+# if MAP_REGEX
+				for (; av[i]; i++)
+					results += del_class_regex(classname, av[i]);
+# else /* MAP_REGEX */
+				return NULL;
+# endif /* MAP_REGEX */
+			}
+			else for (; av[i]; i++)
+				results += del_class(classname, av[i]);
+
+			if (results > EX_OK)
+				return map_rewrite(map, srval, strlen(srval), NULL);
+			else
+				return map_rewrite(map, frval, strlen(frval), NULL);
+			break;
+
+		case 2:
+			for (; av[i]; i++)
+				setclass(mid, av[i]);
+			return map_rewrite(map, frval, strlen(frval), NULL);
+
+			break;
+		
+		case 3:
+			if (av[i])
+			{
+				for (; av[i]; i++)
+					bufdump_class(classname, &buf, &len, &bufsz, delim, av[i], clm_regex);
+			}
+			else
+				bufdump_class(classname, &buf, &len, &bufsz, delim, NULL, 0);
+
+			if (len && buf && buf[len-1] == delim[0])
+				buf[len-1] = '\0';
+			return map_rewrite(map, buf, len, av);
+			break;
+		default:
+			break;
+	}
+	return NULL;
+}
+#endif /* _FFR_CLASS_MAP */
+
 /*
 **  Implicit Modules
 **
diff -urw sendmail-8.13.8-orig/sendmail/sendmail.h sendmail-8.13.8-mr16/sendmail/sendmail.h
--- sendmail-8.13.8-orig/sendmail/sendmail.h	2006-05-22 21:32:07.000000000 -0400
+++ sendmail-8.13.8-mr16/sendmail/sendmail.h	2007-02-06 23:28:08.000000000 -0500
@@ -1104,6 +1117,14 @@
 extern int	strtorwset __P((char *, char **, int));
 extern void	translate_dollars __P((char *));
 extern bool	wordinclass __P((char *, int));
+#if _FFR_CLASS_MAP
+extern int	bufdump_class __P((char *,char **, size_t *, size_t *, char *, char *, int));
+extern int	del_class __P((char *, char *));
+# if MAP_REGEX
+extern int	del_class_regex __P((char *, char *));
+extern int	regex_wordinclass __P((char *, int ));
+# endif /* MAP_REGEX */
+#endif /* _FFR_CLASS_MAP */
 
 /*
 **  Name canonification short circuit.
diff -urw sendmail-8.13.8-orig/sendmail/stab.c sendmail-8.13.8-mr16/sendmail/stab.c
--- sendmail-8.13.8-orig/sendmail/stab.c	2003-05-21 11:36:30.000000000 -0400
+++ sendmail-8.13.8-mr16/sendmail/stab.c	2007-02-06 23:52:18.000000000 -0500
@@ -428,6 +429,455 @@
 	}
 }
 
+#if _FFR_CLASS_MAP
+# if MAP_REGEX
+#  include <regex.h>
+# endif /* MAP_REGEX */
+struct bufdump_Struct {
+	int id;
+	char *buf;
+	char *delim;
+	int delim_len;
+	ssize_t blen;
+	ssize_t bpos;
+	ssize_t num;
+	int (*func) __P((void *, void *));
+	void *func_data;
+};
+
+/*
+**  GEN_STABAPPLY -- apply function to all stab entries
+**
+**	Parameters:
+**		func -- the function to apply.  It will be given two
+**			parameters (the stab entry and the arg).
+**			The function should return EX_OK on success
+**			Any other return value will stop proccessing
+**		arg -- an arbitrary argument, passed to func.
+**
+**	Returns:
+**		EX_OK on successr.
+*/
+
+static int
+gen_stabapply(func, arg)
+	int (*func) __P((STAB *, void *));
+	void *arg;
+{
+	register STAB **shead;
+	register STAB *s;
+	int ret = EX_OK;
+
+	if (!func)
+		return EX_OK - 1;
+
+	for (shead = SymTab; shead < &SymTab[STABSIZE] && ret == EX_OK; shead++)
+	{
+		for (s = *shead; s != NULL && ret == EX_OK; s = s->s_next)
+		{
+			if (tTd(36, 90))
+				sm_dprintf("gen_stabapply: trying %d/%s\n",
+					s->s_symtype, s->s_name);
+			ret = func(s, arg);
+		}
+	}
+	return ret;
+}
+
+/*
+** BUFDUMP_CLASS_APPLY - dumps class values into a buffer, one at a time
+**
+** This function is meant to be called from gen_stabapply()
+**
+**	Parameters:
+**		s - STAB entry
+**		arg - pointer to struct bufdump_Struct
+**
+**	Returns:
+**		EX_OK on success
+**		Anything else on error, terminating gen_stabapply()
+**
+**	Side Effects:
+**		data pointed to by arg may be modified
+**
+*/
+
+static int
+bufdump_class_apply(s, p)
+	register STAB *s;
+	void *p;
+{
+	struct bufdump_Struct *bufdump = (struct bufdump_Struct *)p;
+
+	if (!p)
+		return EX_OK - 1;
+
+	if (!s)
+		return EX_OK - 2;
+
+	if (s->s_symtype != ST_CLASS)
+		return EX_OK;
+
+	if (bitnset(bitidx(bufdump->id), s->s_class))
+	{
+		int slen;
+		
+		if (bufdump->func && (*bufdump->func)(bufdump->func_data, s->s_name))
+			return EX_OK;
+		
+		slen = strlen(s->s_name) + 1 + bufdump->delim_len;
+
+		if (!bufdump->buf)
+		{
+			bufdump->buf = sm_malloc(slen * 2);
+			if (!bufdump->buf)
+			{
+				bufdump->blen = -1;
+				return EX_OK - 3;
+			}
+			bufdump->buf[0] = '\0';
+			bufdump->bpos = 0;
+			bufdump->blen = slen * 2;
+		}
+		else if (bufdump->blen <= (slen + bufdump->bpos))	
+		{
+			char *p1 = sm_realloc(bufdump->buf, (bufdump->blen*2) + (2 * slen)); 
+			if (!p1)
+			{
+				sm_free(bufdump->buf);
+				bufdump->buf = NULL;
+				bufdump->blen = -1;
+				return EX_OK - 4;
+			}
+			bufdump->buf = p1;
+			bufdump->blen = (bufdump->blen * 2) + (2*slen);
+		}
+		memcpy(bufdump->buf + bufdump->bpos, s->s_name, slen - bufdump->delim_len);
+		if (bufdump->delim_len)
+		{
+			memcpy(bufdump->buf + bufdump->bpos + slen - 1 - bufdump->delim_len, 
+						bufdump->delim, bufdump->delim_len); 
+			*(bufdump->buf + bufdump->bpos + slen - 1) = '\0'; 
+		}
+		else
+			*(bufdump->buf + bufdump->bpos + slen) = '\0'; 
+		bufdump->bpos += slen - (bufdump->delim_len > 0);
+		bufdump->num++;
+	}
+	return EX_OK;
+}
+
+/*
+** BUFDUMP_CLASS - dumps class values into a buffer
+**
+**	Parameters:
+**		class -- string describing class
+**		buf -- where to store the values
+**		buflen -- length of data in the buf
+**			 This may be used as an offset for buf for repetitive calls
+**		bufsz -- size of buf
+**		delim -- string describing delimiting character(s) between values
+**			 A value of NULL needs to be used for NUL byte termination
+**		matchstr -- only dump values matching matchstr. Normally this is useless.
+**		regex -- actualy make matchstr usefull, by treating it as a regex
+**			 This requires MAP_REGEX
+**
+**	Returns:
+**		EX_OK + number of values dumped
+**
+**	Side Effects:
+**
+**		buf may be updated to a new location
+**		buflen may be updated to reflect the new length of data in the buf
+**		bufsz may be updated to contain the new size of the buffer
+*/
+static int strcasecmp_wrapper(p1, p2)
+	void *p1;
+	char *p2;
+{
+	char *s1 = p1;
+
+	return(strcasecmp(s1, p2));
+}
+
+# if MAP_REGEX
+static int 
+class_regex_wrapper(p1, p2)
+	void *p1;
+	char *p2;
+{
+	regex_t *class_regex = p1;
+	return(regexec(class_regex, p2, 0, NULL, 0));
+}
+# endif /* MAP_REGEX */
+
+int bufdump_class(class, buf, buflen, bufsz, delim, matchstr, regex)
+	char *class;
+	char **buf;
+	size_t *buflen;
+	size_t *bufsz;
+	char *delim;
+	char *matchstr;
+	int regex;
+{
+	struct bufdump_Struct bufdump;
+	int mid;
+	int status = EX_OK;
+# if MAP_REGEX
+	regex_t class_regex;
+# endif /* MAP_REGEX */
+
+	if (!class || !(mid = macid(class))|| !buf || !bufsz)
+		return EX_OK - 1;
+
+	memset(&bufdump, '\0', sizeof(bufdump));
+	bufdump.id = mid;
+	bufdump.buf = *buf;
+	bufdump.blen = *bufsz;
+	bufdump.bpos = *buflen;
+	bufdump.delim = delim;
+	if (matchstr)
+	{
+		if (regex)
+		{
+# if MAP_REGEX
+			if (!regcomp(&class_regex, matchstr, REG_EXTENDED | REG_ICASE | REG_NOSUB))
+			{
+				bufdump.func = class_regex_wrapper;
+				bufdump.func_data = &class_regex;
+			}
+			else
+			{
+				regfree(&class_regex);
+				return EX_OK - 5;
+			}
+# else /* MAP_REGEX */
+			return EX_OK - 4;
+# endif /* MAP_REGEX */
+		}
+		else
+		{
+			bufdump.func = strcasecmp_wrapper;
+			bufdump.func_data = matchstr;
+		}
+	}
+	if (!bufdump.delim)
+	{
+		bufdump.delim = "";
+		bufdump.delim_len = 1;
+	}
+	else
+		bufdump.delim_len = strlen(delim);
+
+	status = gen_stabapply(bufdump_class_apply,&bufdump);
+# if MAP_REGEX
+	if (matchstr && regex)
+		regfree(bufdump.func_data);
+# endif /* MAP_REGEX */
+	if (status < EX_OK || bufdump.blen < 0)
+		return EX_OK - 2;
+	
+	*buf = bufdump.buf;
+	*bufsz = bufdump.blen;
+	*buflen = bufdump.bpos;
+	return (bufdump.num);
+}
+
+/*
+**  GEN_DEL_CLASS -- remove value from class
+**
+**	Parameters:
+**		class -- the class name
+**		func -- the function to be called
+**		ctx -- opaque context passed to function
+**
+**	Returns:
+**		EX_OK + number of deleted entries on success
+**		EX_OK - 1 on failure
+**
+**	Side Effects:
+**		Removes from Symbol Table entries that are no longer referenced.
+*/
+int
+gen_del_class(class, func, ctx)
+	char *class;
+	int (*func) __P((void *, char *));
+	void *ctx;
+{
+	register STAB **shead;
+	register STAB *s, **ps;
+	int deleted = EX_OK;
+	int mid;
+
+	if (!class || !func || !(mid = macid(class)))
+		return deleted - 1;
+
+	for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
+	{
+		s = *shead;
+		ps = shead;
+		for (; s != NULL; ps = &s->s_next, s = s->s_next)
+		{
+			if (s->s_symtype != ST_CLASS)
+				continue;
+
+			if (!bitnset(bitidx(mid), s->s_class))
+				continue;
+			
+			if ((func)(ctx, s->s_name))
+				continue;
+
+			if (tTd(36, 10))
+				sm_dprintf("del_class: deleting %s from class %s\n",
+					s->s_name, class);
+			deleted++;
+			clrbitn(bitidx(mid), s->s_class);
+
+#if 1
+		/* not sure if this is a good idea or required */
+			{
+				long *lp = (long *) s->s_class;
+				if (!lp[0] && !lp[1] && !lp[2] && !lp[3])
+				{
+					if (tTd(36, 40))
+						sm_dprintf("del_class: freeing %p from SymTab\n", s);
+					*ps = s->s_next;
+					SM_STAB_FREE(s->s_name);
+					sm_free(s);
+				}
+			}
+#endif
+		}
+	}
+	return deleted;
+}
+
+/*
+**  DEL_CLASS -- remove value from class
+**
+**	Parameters:
+**		class -- the class name
+**		str -- the value to remove
+**
+**	Returns:
+**		EX_OK + number of deleted entries on success
+**		EX_OK - 1 on failure
+**
+**	Side Effects:
+**		Removes from Symbol Table entries that are no longer referenced.
+*/
+int
+del_class(class, str)
+	char *class;
+	char *str;
+{
+	return(gen_del_class(class, strcasecmp_wrapper, str));
+}
+
+# if MAP_REGEX
+
+/*
+**  DEL_CLASS_REGEX -- remove value from class matching regex
+**
+**	Parameters:
+**		class -- the class name
+**		regex -- the value to remov
+**
+**	Returns:
+**		EX_OK + number of deleted entries on success
+**		EX_OK - 1 on failure
+**
+**	Side Effects:
+**		Removes from Symbol Table entries that are no longer referenced.
+*/
+int
+del_class_regex(class, str)
+	char *class;
+	char *str;
+{
+	int ret = EX_OK - 1;
+	regex_t class_regex;
+	if (!regcomp(&class_regex, str, REG_EXTENDED | REG_ICASE | REG_NOSUB))
+		ret = gen_del_class(class, class_regex_wrapper, &class_regex);
+	regfree(&class_regex);
+	return ret;
+}
+
+/*
+**  REGEX_WORLDINCLASS -- find word's in class matching regex string
+**
+**	Parameters:
+**		class -- class id.
+**		regex -- regular.expression.
+**
+**	Returns:
+**		EX_OK + numbers of matches on success
+**		< EX_OK on failure
+**	
+*/
+struct regex_wordinclass_Struct
+{
+	int id;
+	regex_t *class_regex;
+	char **matches;
+	int maxmatches;
+	int nmatches;
+};
+
+static int
+regex_wordinclass_apply(s, p)
+	register STAB *s;
+	void *p;
+{
+	struct regex_wordinclass_Struct *r = (struct regex_wordinclass_Struct *)p;
+
+	if (!p)
+		return EX_OK - 1;
+
+	if (!s)
+		return EX_OK - 2;
+
+	if (s->s_symtype != ST_CLASS)
+		return EX_OK;
+
+	if (bitnset(bitidx(r->id), s->s_class))
+	{
+		if (!regexec(r->class_regex, s->s_name, 0, NULL, 0))
+		{
+			r->nmatches++;
+			if (r->maxmatches >= r->nmatches)
+				r->matches[r->nmatches-1] = s->s_name;
+			else
+				return EX_OK - 3;
+		}
+	}
+	return EX_OK;
+}
+
+int
+regex_wordinclass(regex, class)
+	char *regex;
+	int class;
+{
+	int ret = EX_OK - 1;
+	struct regex_wordinclass_Struct s;
+	regex_t class_regex;
+
+	memset(&s, '\0', sizeof(s));
+	s.id = class;
+	s.class_regex = &class_regex;
+	if (!regcomp(&class_regex, regex, REG_EXTENDED | REG_ICASE | REG_NOSUB))
+	{
+		ret = gen_stabapply(regex_wordinclass_apply, &s);
+		ret = s.nmatches;
+	}
+	regfree(&class_regex);
+	return ret;
+}
+# endif /* MAP_REGEX */
+#endif /* _FFR_CLASS_MAP */
+
+
 #if SM_HEAP_CHECK
 /*
 **  DUMPSTAB -- dump symbol table.
