Index: sendmail/conf.c
--- sendmail/conf.c.orig	2014-11-07 19:01:17.000000000 +0100
+++ sendmail/conf.c	2015-01-03 12:26:40.869964710 +0100
@@ -524,6 +524,12 @@
 		ndbm_map_lookup, ndbm_map_store);
 #endif /* NDBM */
 
+#if MYSQLMAP
+	MAPDEF("mysql", NULL, MCF_ALIASOK | MCF_NOTPERSIST,
+		mysql_map_parseargs, mysql_map_open, mysql_map_close,
+		mysql_map_lookup, null_map_store);
+#endif
+
 #if NIS
 	MAPDEF("nis", NULL, MCF_ALIASOK,
 		map_parseargs, nis_map_open, null_map_close,
@@ -5795,6 +5801,9 @@
 #if NDBM
 	"NDBM",
 #endif
+#if MYSQLMAP
+	"MYSQL",
+#endif /* MYSQLMAP */
 #if NETINET
 	"NETINET",
 #endif
Index: sendmail/map.c
--- sendmail/map.c.orig	2014-10-21 14:55:53.000000000 +0200
+++ sendmail/map.c	2015-01-03 12:23:54.010131127 +0100
@@ -1904,12 +1904,443 @@
 }
 
 #endif /* NDBM */
+
+
 /*
-**  NEWDB (Hash and BTree) Modules
+* MySQL map class for Sendmail 8.12.x
+* 
+* (c) 2001 Igmar Palsenberg
+* JDI Media Solutions
+* 
+* MySQL can be obtained from http://www.mysql.com
+*
+* This version is subject to the Sendmail license. Sendmail Inc. is NOT
+* responsible for this code, they don't have anything to do with it, and
+* don't support it.
+*
+* USE AT YOUR OWN RISK.  NO WARRANTY OF ANY KIND IS PROVIDED. PLEASE
+* READ THE INSTRUCTIONS FOR USE OF THIS PATCH BEFORE CONTACTING THE
+* AUTHOR OR SENDMAIL, INC.  NO SUPPORT OF ANY KIND WILL BE PROVIDED
+* BY SENDMAIL, INC. FOR THIS PATCH.
+*
+* Please use the sendmail_mysql@jdimedia.nl adress for questions, comments,
+* remarks, etc, not my personal address.
+*
+* See http://projects.jdimedia.nl for a HOWTO on installing / using it.
+*
+*/ 
+#ifdef MYSQLMAP
+#include <mysql/mysql.h>
+
+struct mysql_conn {
+	char * host;
+	char * user;
+	char * passwd;
+	char * db;
+	int port;
+};
+
+static char * parse_opt_arg(char * in, char * option, char ** value)
+{
+	int len = strlen(in);
+	char * tmp;
+
+	/* Skip whitespaces */
+	while ((*in == ' ') || (*in == '\t') && (*in != '\0'))
+		in++;
+	if (*in == '\0')
+		return NULL;
+	
+	while ((*in != '-') && (*in != '"'))
+		in++;
+
+	/* " is an error in this case */
+	if (*in == '"')
+		return NULL;
+
+	in++;
+	*option = *in;
+	in++;
+	
+	while ((*in == ' ') || (*in == '\t'))
+		in++;
+
+	/* Not a " is an error */
+	if (*in != '"')
+		return NULL;
+	in++;
+	
+	tmp = (char *) sm_malloc(sizeof(char) * len);
+	*value = tmp;
+	
+	while (*in != '"') 
+		*tmp++ = *in++;
+	
+	/* Null terminate */
+	*tmp = '\0';
+	in++;
+	
+	return in;
+}
+
+static char * parse_delim_arg(char * in, char delim, char ** key, char ** value)
+{
+	int len = strlen(in);
+	char * tmp;
+
+	while ((*in == ' ') || (*in == '\t') && (*in != '\0'))
+		in++;
+	if (*in == '\0')
+		return NULL;
+
+	/* Key */
+	tmp = (char *) sm_malloc(sizeof(char) * len);
+	
+	*key = tmp;
+
+	while ((*in != delim) && (*in != '\0') && (*in != ' ') && (*in != '\t'))
+		*tmp++ = *in++;
+
+	if ((*in != delim) ) {
+		free(*key);
+		return NULL;
+	}
+	*tmp = '\0';
+	in++;
+	
+	/* Value */	
+	tmp = (char *) sm_malloc(sizeof(char) * len);
+	*value = tmp;
+
+	while ((*in != '\0') && (*in != ' ') && (*in != '\t'))
+		*tmp++ = *in++;
+	*tmp = '\0';
+
+	return in;	
+}
+
+
+/*
+* Parse MYSQL map definitions. I call it an ugly hack :)
+*
+* return false if failed, true if succeeded
+*
+* Copied from the sendmail generic map_parseargs() code
 */
+bool
+mysql_map_parseargs(map, args)
+	MAP * map;
+	char * args;
+{
+	char *p = args;
+	char option;
+	char * value; 
 
-#if NEWDB
+	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
+	map->map_spacesub = SpaceSub;	/* default value */
+	while (p)
+	{
+		p = parse_opt_arg(p, &option, &value);
+		if (!p)
+			break;
+		switch (option)
+		{
+		  case 'N':		/* Append NULL byte to all keys */
+			map->map_mflags |= MF_INCLNULL;
+			map->map_mflags &= ~MF_TRY0NULL;
+			break;
+
+		  case 'O':		/* Adaptive versus never add NULL */
+			map->map_mflags &= ~MF_TRY1NULL;
+			break;
+
+		  case 'o':		/* Database file is optional */
+			map->map_mflags |= MF_OPTIONAL;
+			break;
+
+		  case 'f':		/* Don't fold keys to lowercase */
+			map->map_mflags |= MF_NOFOLDCASE;
+			break;
+
+		  case 'm':		/* Supress replacement on match */
+			map->map_mflags |= MF_MATCHONLY;
+			break;
+
+		  case 'A':		/* Append values for duplicate keys */
+			map->map_mflags |= MF_APPEND;
+			break;
+
+		  case 'q':		/* Don't strip quotes */
+			map->map_mflags |= MF_KEEPQUOTES;
+			break;
+
+		  case 'a':		/* Append tag on successful match */
+			map->map_app = value;
+			value = NULL;
+			break;
+
+		  case 'T':		/* No idea */	
+			map->map_tapp = value;
+			value = NULL;
+			break;
+
+		  case 'k':		/* No idea */
+			map->map_keycolnm = value;
+			value = NULL;
+			break;
+
+		  case 'v':		/* Specify the value's column */
+			map->map_valcolnm = value;
+			value = NULL;
+			break;
+
+		  case 'z':		/* Column delimiter */
+			if (value[0] != '\\')
+				map->map_coldelim = value[0];
+			else
+			{
+				switch (value[1])
+				{
+				  case 'n':
+					map->map_coldelim = '\n';
+					break;
+
+				  case 't':
+					map->map_coldelim = '\t';
+					break;
+
+				  default:
+					map->map_coldelim = '\\';
+				}
+			}
+			break;
+
+		  case 't':		/* No idea */
+			map->map_mflags |= MF_NODEFER;
+			break;
+
+
+		  case 'S':		/* No idea */
+			map->map_spacesub = value[0];
+			break;
+
+		  case 'D':		/* No idea, I need updated docs :( */
+			map->map_mflags |= MF_DEFER;
+			break;
+
+		  case 'c':
+			map->map_keycolnm = value;
+			value = NULL;
+			break;
+
+		  case 's':
+			map->map_valcolnm = value;
+			value = NULL;
+			break;
 
+		  default:
+			syserr("mysql_map_parse_args : illegal option %c map `%s'", *p, map->map_mname);
+			break;
+		}
+		if (value)
+			sm_free(value);
+	}
+	if (map->map_keycolnm == NULL) {
+		syserr("mysql_map_parse_args : no connect string for MySQL map `%s'", map->map_mname);
+		return false;
+	} 
+
+	if (map->map_valcolnm == NULL) {
+		syserr("mysql_map_parse_args : no select statement for MySQL map `%s'", map->map_mname);
+		return false;
+	}
+	
+	return true;
+}
+
+static bool
+mysql_map_parse_connect(char * cs, struct mysql_conn * conn, MAP * map)
+{
+	char * p;
+	char * key, * value;
+	char * endptr;
+
+	p = cs;	
+
+	memset(conn, '\0', sizeof(struct mysql_conn));
+
+	while (p) {
+		p = parse_delim_arg(p, '=', &key, &value);
+		if (!p)
+			continue;
+		if (strcmp(key, "host") == 0) {
+			conn->host = value;
+		} else if (strcmp(key, "user") == 0) {
+			conn->user = value;
+		} else if (strcmp(key, "passwd") == 0) {
+			conn->passwd = value;
+		} else if (strcmp(key, "db") == 0) {
+			conn->db = value;
+		} else if (strcmp(key, "port") == 0) {
+			conn->port = (int) strtoul(value, &endptr, 10);
+			if (*endptr != '\0')
+				conn->port = 0;
+			sm_free(value);
+		} else {
+			syserr("mysql_map_parse_connect : illegal MySQL option %s map `%s'", key, map->map_mname);
+		}
+		sm_free(key);
+	}
+
+	if ((conn->host == NULL) || (conn->user == NULL) || (conn->passwd == NULL))
+		return false;
+
+	return true;
+}
+
+static void mysql_map_free_conn(struct mysql_conn * conn)
+{
+	if (conn->host)
+		sm_free(conn->host);
+	if (conn->user)
+		sm_free(conn->user);
+	if (conn->passwd)
+		sm_free(conn->passwd);
+	if (conn->db)
+		sm_free(conn->db);
+}
+
+/*
+* Open connection to the MySQL server
+*
+* returns true if all where OK, false if something went wrong
+*
+*/
+bool
+mysql_map_open(map, mode)
+	MAP * map;
+	int mode;
+{
+	MYSQL * mysql;
+	struct mysql_conn conn;
+
+    /* Make sure newaliases doesn't rebuild it */
+	mode &= O_ACCMODE;
+
+	if (mode != O_RDONLY) {
+		errno = EPERM;
+		return false;
+	}
+
+	mysql = mysql_init(NULL);
+
+	if (mysql_map_parse_connect(map->map_keycolnm, &conn, map) == false) {
+		syserr("mysql_map_open : cannot parse map arguments %s for map `%s'", map->map_keycolnm, map->map_mname);
+		mysql_map_free_conn(&conn);
+		return false;
+	}
+
+	if (!mysql_real_connect(mysql, conn.host, conn.user, conn.passwd, conn.db, conn.port, NULL, 0)) {
+		mysql_map_free_conn(&conn);
+		syserr("mysql_map_open : cannot open a MySQL connection for map `%s' : %s", map->map_mname, mysql_error(mysql));
+		return false;	
+	}
+	mysql_map_free_conn(&conn);
+	map->map_db1 = (ARBPTR_T) mysql;
+	map->map_pid = getpid();
+	
+	return true;
+}
+
+/*
+* Close connection to the MYSQL database
+* 
+*
+*/
+void
+mysql_map_close(map)
+	MAP * map;
+{
+	if (map->map_pid == getpid())
+		mysql_close(map->map_db1);
+}
+
+/*
+*
+* Do a map look up.
+*
+* Returns value that comes with the key
+* 
+*/
+char *
+mysql_map_lookup(map, name, av, statp)
+	MAP * map;
+	char * name;
+	char ** av;
+	int * statp;
+{
+	MYSQL * mysql = (MYSQL *) map->map_db1;
+	MYSQL_RES * result;
+	MYSQL_ROW row;		
+	char * query;
+	char * tmp;
+	int len;
+
+	len = strlen(name) + strlen(map->map_valcolnm) + 5;
+	if (len > MAXNAME)
+		return NULL;
+
+	tmp = (char *) sm_malloc(strlen(name) + 1);
+	strncpy(tmp, name, strlen(name) + 1);
+
+	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+		makelower(tmp);
+
+	query = (char *) sm_malloc(sizeof(char) * len);
+	/* Create query */
+	snprintf(query, len, map->map_valcolnm, tmp);
+
+	sm_free(tmp);
+
+	/* Query and retreive rows */	
+	if (mysql_real_query(mysql, query, strlen(query))) {
+		syserr("mysql_map_lookup : query %s : %s", query, mysql_error(mysql));
+		sm_free(query);
+		return NULL;
+	}
+	
+	result = mysql_store_result(mysql);
+	if (result == NULL) {
+		syserr("mysql_map_lookup : store result : %s", mysql_error(mysql));
+		sm_free(query);
+		return NULL;
+	}	
+	
+	/* 0 rows == no result */
+	if (mysql_num_rows(result) == 0) {
+		sm_free(query);
+		return NULL;
+	}
+	
+	row = mysql_fetch_row(result);
+	if (row[0] == NULL || strlen(row[0]) == 0) {
+		sm_free(query);
+		mysql_free_result(result);
+		return NULL;
+	}
+
+	if (bitset(MF_MATCHONLY, map->map_mflags))
+		return map_rewrite(map, name, strlen(name), NULL);
+	else
+		return map_rewrite(map, row[0], strlen(row[0]), av);
+}
+#endif	/* MYSQLMAP */
+
+
+/*
+**  NEWDB (Hash and BTree) Modules
+*/
+#if NEWDB
 /*
 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
 **
