You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

475 lines
9.8 KiB

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.
**