|
|
|
|
--- sendmail/conf.c.orig Tue Oct 16 11:24:13 2001
|
|
|
|
|
+++ sendmail/conf.c Wed Oct 17 18:49:33 2001
|
|
|
|
|
@@ -470,6 +470,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,
|
|
|
|
|
@@ -5381,6 +5387,9 @@
|
|
|
|
|
#if NDBM
|
|
|
|
|
"NDBM",
|
|
|
|
|
#endif /* NDBM */
|
|
|
|
|
+#if MYSQLMAP
|
|
|
|
|
+ "MYSQL",
|
|
|
|
|
+#endif /* MYSQLMAP */
|
|
|
|
|
#if NETINET
|
|
|
|
|
"NETINET",
|
|
|
|
|
#endif /* NETINET */
|
|
|
|
|
--- sendmail/map.c.orig Tue Oct 16 11:22:26 2001
|
|
|
|
|
+++ sendmail/map.c Thu Oct 18 09:34:53 2001
|
|
|
|
|
@@ -1822,12 +1822,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.
|
|
|
|
|
**
|