diff -ruN sendmail-8.14.3/cf/feature/pg_accessdb.m4 sendmail-8.14.3.pgsql/cf/feature/pg_accessdb.m4 --- sendmail-8.14.3/cf/feature/pg_accessdb.m4 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/cf/feature/pg_accessdb.m4 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,19 @@ +divert(-1) +# By using this file, you agree to be cool and share your knowledge +# +# David, http://blue-labs.org/software/sm-pgsql/sendmail.php +# + +divert(0) +VERSIONID(`$Id: pg_accessdb.m4,v 2.0 2000/07/19 18:15:16 blu3 Exp $') +divert(-1) + +define(`_ACCESS_TABLE_', `') +define(`_PG_ACCESS_TABLE_', `') +define(`_TAG_DELIM_', `:')dnl should be in OperatorChars + +LOCAL_CONFIG +# PostgreSQL based access list +Kaccess ifelse(defn(`_ARG_'), `', + DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`access', + `_ARG_') diff -ruN sendmail-8.14.3/cf/feature/pg_aliases.m4 sendmail-8.14.3.pgsql/cf/feature/pg_aliases.m4 --- sendmail-8.14.3/cf/feature/pg_aliases.m4 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/cf/feature/pg_aliases.m4 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,19 @@ +divert(-1) +# By using this file, you agree to be cool and share your knowledge +# +# David, http://blue-labs.org/software/sm-pgsql/sendmail.php +# + +divert(0) +VERSIONID(`$Id: pg_aliases.m4,v 2.0 2000/07/19 18:15:16 blu3 Exp $') +divert(-1) + +undefine(`ALIAS_FILE') +define(`_ALIASES_TABLE_', `') +define(`_PG_ALIASES_TABLE_', `') + +LOCAL_CONFIG +# PostgreSQL based local aliases +Kaliases ifelse(defn(`_ARG_'), `', + DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`aliases', + `_ARG_') diff -ruN sendmail-8.14.3/cf/feature/pg_domaintable.m4 sendmail-8.14.3.pgsql/cf/feature/pg_domaintable.m4 --- sendmail-8.14.3/cf/feature/pg_domaintable.m4 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/cf/feature/pg_domaintable.m4 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,18 @@ +divert(-1) +# By using this file, you agree to be cool and share your knowledge +# +# David, http://blue-labs.org/software/sm-pgsql/sendmail.php +# + +divert(0) +VERSIONID(`$Id: pg_domaintable.m4,v 2.0 2000/07/19 18:15:16 blu3 Exp $') +divert(-1) + +define(`_DOMAIN_TABLE_', `') +define(`_PG_DOMAIN_TABLE', `') + +LOCAL_CONFIG +# PostgreSQL based domain rewriting table +Kdomaintable ifelse(defn(`_ARG_'), `', + DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`domaintable', + `_ARG_') diff -ruN sendmail-8.14.3/cf/feature/pg_generics.m4 sendmail-8.14.3.pgsql/cf/feature/pg_generics.m4 --- sendmail-8.14.3/cf/feature/pg_generics.m4 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/cf/feature/pg_generics.m4 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,18 @@ +divert(-1) +# By using this file, you agree to be cool and share your knowledge +# +# David, http://blue-labs.org/software/sm-pgsql/sendmail.php +# + +divert(0) +VERSIONID(`$Id: pg_genericstable.m4,v 2.0 2000/07/19 18:15:16 blu3 Exp $') +divert(-1) + +define(`_GENERICS_TABLE_', `') +define(`_PG_GENERICS_TABLE', `') + +LOCAL_CONFIG +# PostgreSQL based generic domain mapping table, similar to userdb +Kgenerics ifelse(defn(`_ARG_'), `', + DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`generics', + `_ARG_') diff -ruN sendmail-8.14.3/cf/feature/pg_mailer.m4 sendmail-8.14.3.pgsql/cf/feature/pg_mailer.m4 --- sendmail-8.14.3/cf/feature/pg_mailer.m4 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/cf/feature/pg_mailer.m4 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,18 @@ +divert(-1) +# By using this file, you agree to be cool and share your knowledge +# +# David, http://blue-labs.org/software/sm-pgsql/sendmail.php +# + +divert(0) +VERSIONID(`$Id: pg_mailertable.m4,v 2.0 2000/07/19 18:15:16 blu3 Exp $') +divert(-1) + +define(`_MAILER_TABLE_', `') +define(`_PG_MAILER_TABLE', `') + +LOCAL_CONFIG +# PostgreSQL based mailer table, for overriding domain and MX +Kmailertable ifelse(defn(`_ARG_'), `', + DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`mailertable', + `_ARG_') diff -ruN sendmail-8.14.3/cf/feature/pg_usersdb.m4 sendmail-8.14.3.pgsql/cf/feature/pg_usersdb.m4 --- sendmail-8.14.3/cf/feature/pg_usersdb.m4 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/cf/feature/pg_usersdb.m4 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,18 @@ +divert(-1) +# By using this file, you agree to be cool and share your knowledge +# +# David, http://blue-labs.org/software/sm-pgsql/sendmail.php +# + +divert(0) +VERSIONID(`$Id: pg_usersdb.m4,v 2.0 2000/07/19 18:15:16 blu3 Exp $') +divert(-1) + +define(`_REWRITE_TABLE_', `') +define(`_PG_REWRITE_TABLE_', `') + +LOCAL_CONFIG +# PostgreSQL based user rewrite table (can turn "John.Doe" into "jdoe") +Kusersdb ifelse(defn(`_ARG_'), `', + DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`usersdb', + `_ARG_') diff -ruN sendmail-8.14.3/cf/feature/pg_virtualusers.m4 sendmail-8.14.3.pgsql/cf/feature/pg_virtualusers.m4 --- sendmail-8.14.3/cf/feature/pg_virtualusers.m4 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/cf/feature/pg_virtualusers.m4 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,18 @@ +divert(-1) +# By using this file, you agree to be cool and share your knowledge +# +# David, http://blue-labs.org/software/sm-pgsql/sendmail.php +# + +divert(0) +VERSIONID(`$Id: pg_virtusertable.m4,v 2.0 2000/07/19 18:15:16 blu3 Exp $') +divert(-1) + +define(`_VIRTUSER_TABLE_', `') +define(`_PG_VIRTUSER_TABLE_', `') + +LOCAL_CONFIG +# PostgreSQL based virtual user table (maps incoming users) +Kvirtuser ifelse(defn(`_ARG_'), `', + DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`virtusertable', + `_ARG_') diff -ruN sendmail-8.14.3/pgsql-build.mc sendmail-8.14.3.pgsql/pgsql-build.mc --- sendmail-8.14.3/pgsql-build.mc 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/pgsql-build.mc 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,32 @@ +###################################################################### +# This is the Blue Labs m4 file for the Sendmail w/ Postgres Tables patch. +# +# The website for building sendmail with this patch is at +# http://blue-labs.org/software/sm-pgsql/sendmail.php +# +# This patch may eventually be available to Gentoo (http://gentoo.org/) +# users with the "USE=postgres" use flag +# +# !!! EDIT THE CONFIGURATION SETTINGS TO MATCH YOUR NEEDS !!! +# +# NOTE: This is a specialised .cf for the postgresql patch only +# +VERSIONID(`(#) Blue Labs bluelabs.mc v 11.1 (Blue Labs) 12 Jan 2007') +###################################################################### + +## +# See http://blue-labs.org/software/sm-pgsql/sendmail.php for detailed +# instructions +# +# TODO: replace 'newaliases' and 'makemap' targets with "don't run this" +# update man pages also +## + +# uncomment these two lines if you use /usr/local/pgsql +# APPENDDEF(`confINCDIRS',`-I/usr/local/pgsql/include')dnl +# APPENDDEF(`confLIBDIRS',`-L/usr/local/pgsql/lib')dnl + +APPENDDEF(`confLIBS',`-lpq')dnl +APPENDDEF(`confMAPDEF',`-DPGSQLMAP')dnl +APPENDDEF(`DATABASE_MAP_TYPE', `pgsql')dnl + diff -ruN sendmail-8.14.3/pgsql-config.mc sendmail-8.14.3.pgsql/pgsql-config.mc --- sendmail-8.14.3/pgsql-config.mc 1969-12-31 16:00:00.000000000 -0800 +++ sendmail-8.14.3.pgsql/pgsql-config.mc 2007-03-26 13:19:46.000000000 -0700 @@ -0,0 +1,51 @@ +###################################################################### +# This is the Blue Labs m4 file for the Sendmail w/ Postgres Tables patch. +# +# The website for building sendmail with this patch is at +# http://blue-labs.org/software/sm-pgsql/sendmail.php +# +# This patch may eventually be available to Gentoo (http://gentoo.org/) +# users with the "USE=postgres" use flag +# +# !!! EDIT THE CONFIGURATION SETTINGS TO MATCH YOUR NEEDS !!! +# +# NOTE: This is a specialised .cf for the postgresql patch only +# +VERSIONID(`(#) Blue Labs bluelabs.mc v 12.0 (Blue Labs) 12 Dec 2007') +###################################################################### + +## +# See http://blue-labs.org/software/sm-pgsql/sendmail.php for detailed +# instructions +## + +dnl ## +dnl # example line: +dnl # pgsql [-h ] -c -s +dnl ## + +# +# PgSql +# +# add the flag -f to force case sensitive queries, by default both the +# search and the value will be folded to lower case. by default %s will be +# rewritten to by lower('%s'). if -f is specified, then it will only be +# written as ('%s') and both the input value and the row value MUST match +# case exactly. +# + +define(`PG_CONNSTR', "host=localhost dbname=sendmail user=sendmail password=raisins") +define(`SELECT', "select distinct s_out from ) +define(`WHERE', where ``lower'' (s_in) = %s") +FEATURE(`pg_aliases', pgsql -c `PG_CONNSTR' -s `SELECT' `aliases' `WHERE')dnl +FEATURE(`pg_virtualusers', pgsql -c `PG_CONNSTR' -s `SELECT' `virtualusers' `WHERE')dnl +FEATURE(`pg_usersdb', pgsql -c `PG_CONNSTR' -s `SELECT' `userrewrite' `WHERE')dnl +FEATURE(`pg_accessdb', pgsql -c `PG_CONNSTR' -s `SELECT' `access' `WHERE')dnl +FEATURE(`pg_domaintable', pgsql -c `PG_CONNSTR' -s `SELECT' `domaintable' `WHERE')dnl +FEATURE(`pg_generics', pgsql -c `PG_CONNSTR' -s `SELECT' `genericstable' `WHERE')dnl +FEATURE(`pg_mailer', pgsql -c `PG_CONNSTR' -s `SELECT' `mailertable' `WHERE')dnl + +define(`confPROCESS_TITLE_PREFIX', `[Blue-PgSQL]')dnl +define(`confSMTP_LOGIN_MSG', `Mail server\ + \ + Unauthorized use prohibited\ + \ + Spam is prohibited here and any detected spam may be used in prosecution\ + against the spammer. This setup uses PgSQL (postgresql) for most of it''`s\ + tables, for information on this, see\ + http://blue-labs.org/software/sm-pgsql/sendmail.php\ + Don''`t bitch if your MTA is busted and doesn''`t play well with others.\ + \ +')dnl + +dnl # Don't attempt to rebuild an aliases file, sm-pgsql doesn't use files +dnl # however if you have multiple alias tables and DO use files, remove this +dnl # next line +define(`confALIAS_WAIT', `0')dnl diff -ruN sendmail-8.14.3/sendmail/README sendmail-8.14.3.pgsql/sendmail/README --- sendmail-8.14.3/sendmail/README 2006-11-13 14:27:27.000000000 -0800 +++ sendmail-8.14.3.pgsql/sendmail/README 2007-03-26 13:19:46.000000000 -0700 @@ -119,6 +119,11 @@ have to install the UMich or OpenLDAP (http://www.openldap.org/) ldap and lber libraries to use this flag. +PGSQLMAP PostgreSQL SQL query support. You will need to have + PostgreSQL include files and libraries installed to use this + feature. Information on PostgreSQL can be found at + http://www.postgresql.org/ Support for this map may be + found at http://blue-labs.org/software/sm-pgsql/sendmail.php MAP_REGEX Regular Expression support. You will need to use an operating system which comes with the POSIX regex() routines or install a regexp library such as libregex from --- sendmail-8.16.1/sendmail/conf.c.orig 2020-06-04 08:27:49.000000000 +0200 +++ sendmail-8.16.1/sendmail/conf.c 2020-08-29 10:15:17.896341069 +0200 @@ -576,6 +576,12 @@ ldapmap_lookup, null_map_store); #endif +#ifdef PGSQLMAP + MAPDEF("pgsql", NULL, MCF_ALIASOK|MCF_NOTPERSIST, + pgsql_map_parseargs, pgsql_map_open, pgsql_map_close, + pgsql_map_lookup, null_map_store); +#endif + #if PH_MAP MAPDEF("ph", NULL, MCF_NOTPERSIST, ph_map_parseargs, ph_map_open, ph_map_close, @@ -5864,6 +5870,9 @@ #if LDAP_REFERRALS "LDAP_REFERRALS", #endif +#if PGSQLMAP + "PGSQLMAP", +#endif /* PGSQLMAP */ #if LOG "LOG", #endif --- sendmail-8.14.3/sendmail/map.c 2007-10-09 20:06:45.000000000 -0400 +++ sendmail-8.14.3.pgsql/sendmail/map.c 2009-11-09 19:54:47.214965897 -0500 @@ -4706,6 +4706,588 @@ } } #endif /* LDAPMAP */ + +#ifdef PGSQLMAP +#include + +/* + * PostgreSQL map functionality for Sendmail 8.14.3 + * Portions Copyright (C) 2000 Jonathan Yarden + * Remainder copyright (c) 2000-2009 David Ford + * + * For information on PostgreSQL, visit http://www.pgsql.com/ + * Information on this patch and setup is at + * http://blue-labs.org/software/sm-pgsql/sendmail.php + * + * This patch, because it integrates with and is based on the existing + * prior work of Sendmail, is considered by me to be a "derivative + * work" subject to the Sendmail licensing terms. Sendmail, Inc. in not + * responsible for this code. + * + * 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. + */ + +/* +struct pgsqlmap +{ + struct pgsqlmap *p,*n; + char *connstr; + PGconn *conn; + pid_t opener_pid; + char *mapname; +} *pgsqlmap = NULL; */ + +char *pgsql_skip_quotes(char *p) +{ + p = strchr(p,'"'); + if (p) + { + p++; + p = strchr(p,'"'); + if (p) p++; + } + return p; +} + +/* + * Parse PostgreSQL map definition args. + * + * Nothing really special here, since to be perfectly honest, I have never + * seen or used almost all of these options. Most of this code was taken + * directly from existing Sendmail source code. + */ +bool pgsql_map_parseargs(MAP *map, char *args) +{ + register char *p = args; + register int done; + + map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL; + for (;;) + { + while (isascii(*p) && isspace(*p)) + p++; + if (*p != '-') + break; + switch (*++p) + { + case 'A': + map->map_mflags |= MF_APPEND; + break; + + case 'N': + map->map_mflags |= MF_INCLNULL; + map->map_mflags &= ~MF_TRY0NULL; + break; + + case 'O': + map->map_mflags &= ~MF_TRY1NULL; + break; + + case 'T': + map->map_tapp = ++p; + break; + + case 'a': + map->map_app = ++p; + break; + + case 'f': + map->map_mflags |= MF_NOFOLDCASE; + break; + + case 'h': + map->map_db2 = ++p; + break; + + case 'm': + map->map_mflags |= MF_MATCHONLY; + break; + + case 'o': + map->map_mflags |= MF_OPTIONAL; + break; + + case 'q': + map->map_mflags |= MF_KEEPQUOTES; + break; + + case 't': + map->map_mflags |= MF_NODEFER; + break; + +/* + * Start of PostgreSQL specific args. I cheat and use some existing + * Sendmail variables here since this map class makes no other use of them. + */ + + case 'c': /* connection string */ + map->map_keycolnm = ++p; + p = pgsql_skip_quotes(p); + break; + + case 's': /* select statement */ + map->map_valcolnm = ++p; + p = pgsql_skip_quotes(p); + break; + } + + if (*p != '\0') + *p++ = '\0'; + } + + if (map->map_app != NULL) + map->map_app = newstr(map->map_app); + + if (map->map_tapp != NULL) + map->map_tapp = newstr(map->map_tapp); + + if (map->map_keycolnm != NULL) { /* database connect string */ + map->map_keycolnm = newstr(map->map_keycolnm); + stripquotes(map->map_keycolnm); + p = map->map_keycolnm; + while(*p==' ') + p++; + map->map_keycolnm = p; + } else { + sm_syslog(LOG_WARNING, CurEnv->e_id, "No PostgreSQL connect string for %s map %s", + map->map_class->map_cname, map->map_mname); + syserr("451 4.3.5 No PostgreSQL connect string for map %s", + map->map_mname); + SM_SET_H_ERRNO(TRY_AGAIN); + errno = DB_NOTFOUND; + return false; + } + + if (map->map_valcolnm != NULL) { /* select statement */ + map->map_valcolnm = newstr(map->map_valcolnm); + stripquotes(map->map_valcolnm); + p = map->map_valcolnm; + while(*p==' ') + p++; + map->map_valcolnm = p; + } else { + sm_syslog(LOG_WARNING, CurEnv->e_id, "No PostgreSQL select statement for %s map %s", + map->map_class->map_cname, map->map_mname); + syserr("451 5.3.5 No PostgreSQL select statement for map %s", + map->map_mname); + SM_SET_H_ERRNO(TRY_AGAIN); + errno = DB_NOTFOUND; + return false; + } + + return true; +} + +#if 0 +/* + * search our list of map connections for a entry that matches our + * connection. it should have the same host and connection string, as well + * as the same PID. someone can choose to put their maps on different + * databases, so we cannot share a connection. + */ + +PGconn *pgsql_getconn(char *connstr, char *mapname) +{ + pid_t p = getpid(); + + // if no map has been allocated, return immediately + if(!pgsqlmap) return NULL; + + // rewind + while(pgsqlmap->p) pgsqlmap = pgsqlmap->p; + + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "pgsql_getconn (connection string:%s, pid:%i)", connstr, p); + #endif + + // only return connections that match everything + do + { + if(pgsqlmap->conn) + if (!strncmp(connstr, pgsqlmap->connstr, strlen(connstr)+1)) + if (pgsqlmap->opener_pid == p) + if (!strncmp(pgsqlmap->mapname, mapname, strlen(mapname)) + return pgsqlmap->conn; + + // if there is more than one map and we're still searching, bump to the next one + if (pgsqlmap->n) + pgsqlmap = pgsqlmap->n; + else + break; + } while(1); + + return NULL; +} + +/* + * add a new map entry structure and populate it + */ +void pgsql_addconn(char *host, char *connstr, PGconn *conn, mapname) +{ + int count=1; + + if (!pgsqlmap) + // create a brand new map structure + pgsqlmap= (struct pgsqlmap *) xalloc(sizeof(struct pgsqlmap)); + + else + { + // fast-forward to the end of the list + while(pgsqlmap->n) + { + count++; + pgsqlmap = pgsqlmap->n; + } + + pgsqlmap->n = (struct pgsqlmap *) xalloc(sizeof(struct pgsqlmap)); + pgsqlmap->n->p = pgsqlmap; + pgsqlmap = pgsqlmap->n; + } + + bzero(pgsqlmap, sizeof(struct pgsqlmap)); + pgsqlmap->connstr =connstr; + pgsqlmap->conn =conn; + pgsqlmap->opener_pid =getpid(); + pgsqlmap->mapname =mapname; + + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "pgsql_ add map connection (connection string:%s, connptr:%p, pid:%i), maplist size is %i", + pgsqlmap->connstr, pgsqlmap->conn, pgsqlmap->opener_pid, count); + #endif +} + +int psql_removeconn(PGconn *conn, char *mapname) +{ + if(!pgsqlmap) return 0; + + while(pgsqlmap->p) pgsqlmap = pgsqlmap->p; + + do + { + if (pgsqlmap->conn == conn && !strncmp(pgsqlmap->mapname, mapname, strlen(mapname)) + { + // the map data doesn't belong to us, just deallocate the map pointer + struct pgsqlmap *z = pgsqlmap; + + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "pgsql_ delete map connection (connection string:%s, connptr:%p, pid:%i)", + pgsqlmap->connstr, pgsqlmap->conn, pgsqlmap->opener_pid); + #endif + + if (z->p && z->n) + { + z->p->n = z->n; + z->n->p = z->p; + free(z); + pgsqlmap = pgsqlmap->n; + return 1; + } + + else if (z->p) + { + z->p->n = NULL; + free(z); + pgsqlmap = pgsqlmap->p; + return 1; + } + + else if (z->n) + { + z->n->p = NULL; + free(z); + pgsqlmap = pgsqlmap->n; + return 1; + } + + // only return 1 if no map references are left so the pgsql connection can be shut down + else + { + free(z); + pgsqlmap = NULL; + return 1; + } + + } + + if (pgsqlmap->n) + pgsqlmap = pgsqlmap->n; + else + break; + } while(1); + + // no maps found + return 0; +} +#endif + +/* + * Open a PostgreSQL database connection using the connection string, the + * mode parameter is entirely ignored - it has no purpose in SQL. + * + * Returns TRUE if the database was opened or FALSE if it choked + */ +bool pgsql_map_open(MAP *map, int mode) +{ + char *s= NULL; + int n, retries; + + /* + * each map can have a different connection, so we can't easily share + * it. further, each connection is protected between thread instances. + */ + + /* + * this is the most likely test so it comes first. we have a connection + * to the server, if the status appears good, return. note: the status + * may be misleading, the server may have crashed underneath us or + * terminated our connection for any of several reasons. we won't know + * that until we actually use it however. + */ + if (map->map_db1 && PQstatus(map->map_db1) == CONNECTION_OK) + { + // the connection is shared between all maps, we don't need to differentiate + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "Connection is good (connection string:%s, connptr:%p)", + map->map_keycolnm, map->map_db1); + #endif + + return true; + } + + #ifdef PGSQLDEBUG + if (!map->map_db1 || PQstatus(map->map_db1) == CONNECTION_BAD) + sm_syslog(LOG_INFO, CurEnv->e_id, "Connection to SQL server not found or stale for map:%s, looking for a new one", map->map_mname); + #endif + + retries=10; + while ((!map->map_db1 || PQstatus(map->map_db1) == CONNECTION_BAD) && retries-- >0) + { + /* + * now either conn is still null (not yet allocated), or our connection + * is bad. synchronous connections only have _OK or _BAD. looks like + * we have to reset or make a new connection. allocate space and try to + * connect + */ + if (!map->map_db1) + { + map->map_db1 = PQconnectdb(map->map_keycolnm); + PQsetErrorVerbosity(map->map_db1, PQERRORS_VERBOSE); + //pgsql_addconn(map->map_keycolnm, map->map_db1, map->map_mname); + map->map_pid = getpid(); /* save PID for check on close */ + } + + else + PQreset(map->map_db1); + + if (!map->map_db1) + { + sm_syslog(LOG_WARNING, CurEnv->e_id, "Cannot open %s map %s using %s, attempt %i", + map->map_class->map_cname, map->map_mname, map->map_keycolnm, 10-retries); + } + + if (PQstatus(map->map_db1) == CONNECTION_BAD) + { + sm_syslog(LOG_WARNING, CurEnv->e_id, "Cannot open %s map %s using %s (%s), attempt %i", + map->map_class->map_cname, map->map_mname, map->map_keycolnm, + PQerrorMessage(map->map_db1), 10-retries); + } + } + + if (!map->map_db1 || PQstatus(map->map_db1) == CONNECTION_BAD) + { + syserr("451 4.3.5 Map database unreachable"); + if (map->map_db1) + { + //psql_removeconn(conn, map->map_mname); + PQfinish(map->map_db1); + map->map_db1 = NULL; + } + + errno = DB_NOTFOUND; + SM_SET_H_ERRNO(TRY_AGAIN); + + return false; + } + + return true; +} + +/* + * Close the PostgreSQL database connection, check that the opening process + * is the closing process; ignore if not. + */ +void pgsql_map_close(MAP *map) +{ + int r; + + if (map->map_pid == getpid()) + { + //conn = map->map_db1; +/* + r = psql_removeconn(conn, map->map_mname); + if(r) + { +*/ PQfinish(map->map_db1); + map->map_db1 = NULL; + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "Closed connection for map %s", map->map_mname); + #endif +/* } + + #ifdef PGSQLDEBUG + else + sm_syslog(LOG_INFO, CurEnv->e_id, "No connection found for map: %s", map->map_mname); + #endif +*/ } +} + +/* +** PGSQL_MAP_LOOKUP -- look up a datum in a PGSQL map +** +** Attempt to map an incoming key value with a PostgreSQL query. +** +** This performs the query specified in the Sendmail config file and +** uses the value of the first row and column as the map data value. +** All other rows and columns are ignored. +*/ +char * +pgsql_map_lookup(MAP *map, char *name, char **av, int *statp) +{ + int len,esc_len,ntuples,r,retries; + char *format,*workingbuf,*trustedbuf; + char sname[MAXNAME+1]; + char resbuf[MAXNAME+1]; + PGresult *res; + + /* + * Check the backend to make sure it's still valid. If it's not, try and + * reset the connection. + */ + if (!map->map_db1 || PQstatus(map->map_db1) == CONNECTION_BAD) + { + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, NOQID, "Connection bad for map: %s, reconnecting to SQL server using: %s", map->map_mname, map->map_keycolnm); + #endif + if (!pgsql_map_open(map, 0)) + { + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "dbg> aborting map lookup"); + #endif + return NULL; + } + } + + bzero(sname,sizeof sname); + bzero(resbuf,sizeof resbuf); + + /* Buffer overflow check. */ + len = strlen(name); + if (len > MAXNAME) + len = MAXNAME; + bcopy(name,sname,len); + + if (!bitset(MF_NOFOLDCASE, map->map_mflags)) + { + //#ifdef PGSQLDEBUG + //sm_syslog(LOG_INFO, CurEnv->e_id, "dbg> making querystring value lowercase"); + //#endif + makelower(sname); + } + + /* Allocate query buffer (select statement + key value) */ + len = strlen(map->map_valcolnm); + esc_len = strlen(sname); + workingbuf = xalloc(esc_len*2+1+9); + trustedbuf = xalloc(len+(esc_len*2)+1+9); + bzero(trustedbuf,len); + + // escape untrusted data passed to us from network + PQescapeStringConn(map->map_db1, trustedbuf, sname, strlen(sname), NULL); + + // if MF_NOFOLDCASE is set, don't wrap this in lower() + if (!bitset(MF_NOFOLDCASE, map->map_mflags)) + format="lower('%s')"; + else + format="'%s'"; + + sprintf(workingbuf, format, trustedbuf); + sprintf(trustedbuf, map->map_valcolnm, workingbuf); + free(workingbuf); + + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "dbg-sql> %s", trustedbuf); + #endif + + retries = 2; + while (retries--) + { + res = PQexec(map->map_db1, trustedbuf); + if (PQresultStatus(res) == PGRES_TUPLES_OK) + break; + + sm_syslog(LOG_WARNING, CurEnv->e_id, "Cannot query PGSQL database (attempt:%i) \"%s\" using \"%s\" because of: %s", + 10-retries, map->map_keycolnm, trustedbuf, PQresultErrorMessage(res)); + PQclear(res); + + // try to reset connection + if (!pgsql_map_open(map, 0)) + { + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "dbg> aborting map lookup"); + #endif + return NULL; + } + } + + if (!map->map_db1 || PQresultStatus(res) != PGRES_TUPLES_OK) + { + syserr("451 4.3.1 Cannot query PGSQL database"); + SM_SET_H_ERRNO(TRY_AGAIN); + errno = DB_NOTFOUND; + free(trustedbuf); + return NULL; + } + + /* + * See if anything came back. If no rows were returned, nothing there for + * this query. + */ + ntuples = PQntuples(res); + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "dbg> finishing map lookup, ntuples:%i", ntuples); + #endif + + if (ntuples <= 0) + { + PQclear(res); + return NULL; + } + + /* + * Get the result from column 0 and clear the rest of the result. + * If the result data is too big, then it's truncated. + */ + len = PQgetlength(res,0,0); + if (len > MAXNAME) + len = MAXNAME; + bcopy(PQgetvalue(res,0,0),resbuf,len); + PQclear(res); + + /* Process results like the other map classes do. */ + + #ifdef PGSQLDEBUG + sm_syslog(LOG_INFO, CurEnv->e_id, "dbg> finishing map lookup, result:\"%s\"", resbuf); + #endif + + if (bitset(MF_MATCHONLY, map->map_mflags)) + return map_rewrite(map, name, strlen(name), NULL); + else + return map_rewrite(map, resbuf, len, av); +} +#endif /* PGSQLMAP */ /* ** PH map */