diff -ruN snapshot-20011210-vanilla/PGSQL_README snapshot-20011210/PGSQL_README --- snapshot-20011210-vanilla/PGSQL_README Thu Jan 1 01:00:00 1970 +++ snapshot-20011210/PGSQL_README Wed Dec 19 10:27:49 2001 @@ -0,0 +1,88 @@ +[Code contributed by Mathieu Arnold] + +We've written code to add a pgsql map type. It utilizes the pgsql +client library, which can be obtained from: + + http://www.postgresql.org/ + +In order to build postfix with pgsql map support, you will need to add +-DHAS_PGSQL and -I for the directory containing the postgres headers, and +the libpq library (and libcrypt) to AUXLIBS, for example: + +make -f Makefile.init makefiles \ + 'CCARGS=-DHAS_PGSQL -I/some/where/include/postgresql' \ + 'AUXLIBS=/some/where/lib/postgres/libpq.a -lcrypt' + +then, just run 'make'. + +Postfix installations which may benefit from using pgsql map types +include sites that have a need for instantaneous updates of +forwarding, and sites that may benefit from having mail exchangers +reference a networked database, possibly working in conjunction with a +customer database of sorts. + +Once postfix is built with pgsql support, you can specify a map type +in main.cf like this: + +alias_maps = pgsql:/etc/postfix/pgsql-aliases.cf + +The file /etc/postfix/pgsql-aliases.cf specifies lots of information +telling postfix how to reference the postgresql database. An example +postgresql map config file follows: + +# +# postgresql config file for alias lookups on postfix +# comments are ok. +# + +# the user name and password to log into the pgsql server +user = someone +password = some_passwordd + +# the database name on the servers +dbname = customer_database + +# the table name +table = mxaliases + +# +select_field = forw_addr +where_field = alias + +# you may specify additional_conditions here +additional_conditions = and status = 'paid' + +# the above variables will result in a query of +# the form: +# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid' +# ($lookup is escaped so if it contains single quotes or other odd +# characters, it will not cause a parse error in the sql). +# +# the hosts that postfix will try to connect to +# and query from (in the order listed) +hosts = host1.some.domain host2.some.domain + +# end postgresql config file + +Some notes: + +This configuration interface setup allows for multiple postgresql +databases: you can use one for a virtual table, one for an access +table, and one for an aliases table if you want. + +Since sites that have a need for multiple mail exchangers may enjoy +the convenience of using a networked mailer database, but do not want +to introduce a single point of failure to their system, we've included +the ability to have postfix reference multiple hosts for access to a +single pgsql map. This will work if sites set up mirrored pgsql +databases on two or more hosts. Whenever queries fail with an error +at one host, the rest of the hosts will be tried in order. Each host +that is in an error state will undergo a reconnection attempt every so +often, and if no pgsql server hosts are reachable, then mail will be +deferred until atleast one of those hosts is reachable. + +Performance of postfix with pgsql has not been thoroughly tested, +however, we have found it to be stable. Busy mail servers using pgsql +maps will generate lots of concurrent pgsql clients, so the pgsql +server(s) should be run with this fact in mind. Any further +performance information, in addition to any feedback is most welcome. diff -ruN snapshot-20011210-vanilla/src/util/Makefile.in snapshot-20011210/src/util/Makefile.in --- snapshot-20011210-vanilla/src/util/Makefile.in Mon Dec 10 00:11:49 2001 +++ snapshot-20011210/src/util/Makefile.in Wed Dec 19 10:28:39 2001 @@ -4,7 +4,7 @@ clean_env.c close_on_exec.c concatenate.c ctable.c dict.c \ dict_alloc.c dict_db.c dict_dbm.c dict_debug.c dict_env.c \ dict_ht.c dict_ldap.c dict_mysql.c dict_ni.c dict_nis.c \ - dict_nisplus.c dict_open.c dict_pcre.c dict_regexp.c dict_static.c \ + dict_nisplus.c dict_open.c dict_pcre.c dict_pgsql.c dict_regexp.c dict_static.c \ dict_tcp.c dict_unix.c dir_forest.c doze.c duplex_pipe.c \ environ.c events.c exec_command.c fifo_listen.c fifo_trigger.c \ file_limit.c find_inet.c fsspace.c fullname.c get_domainname.c \ @@ -32,7 +32,7 @@ clean_env.o close_on_exec.o concatenate.o ctable.o dict.o \ dict_alloc.o dict_db.o dict_dbm.o dict_debug.o dict_env.o \ dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \ - dict_nisplus.o dict_open.o dict_pcre.o dict_regexp.o dict_static.o \ + dict_nisplus.o dict_open.o dict_pcre.o dict_pgsql.o dict_regexp.o dict_static.o \ dict_tcp.o dict_unix.o dir_forest.o doze.o duplex_pipe.o \ environ.o events.o exec_command.o fifo_listen.o fifo_trigger.o \ file_limit.o find_inet.o fsspace.o fullname.o get_domainname.o \ @@ -58,7 +58,7 @@ HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \ connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \ dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \ - dict_nisplus.h dict_pcre.h dict_regexp.h dict_static.h dict_tcp.h \ + dict_nisplus.h dict_pcre.h dict_pgsql.h dict_regexp.h dict_static.h dict_tcp.h \ dict_unix.h dir_forest.h events.h exec_command.h find_inet.h \ fsspace.h fullname.h get_domainname.h get_hostname.h hex_quote.h \ htable.h inet_addr_host.h inet_addr_list.h inet_addr_local.h \ @@ -550,6 +550,8 @@ dict_ldap.o: sys_defs.h dict_mysql.o: dict_mysql.c dict_mysql.o: sys_defs.h +dict_pgsql.o: dict_pgsql.c +dict_pgsql.o: sys_defs.h dict_ni.o: dict_ni.c dict_ni.o: sys_defs.h dict_nis.o: dict_nis.c @@ -590,6 +592,7 @@ dict_open.o: dict_ni.h dict_open.o: dict_ldap.h dict_open.o: dict_mysql.h +dict_open.o: dict_pgsql.h dict_open.o: dict_pcre.h dict_open.o: dict_regexp.h dict_open.o: dict_static.h diff -ruN snapshot-20011210-vanilla/src/util/dict_open.c snapshot-20011210/src/util/dict_open.c --- snapshot-20011210-vanilla/src/util/dict_open.c Sun Dec 9 23:58:02 2001 +++ snapshot-20011210/src/util/dict_open.c Wed Dec 19 10:27:49 2001 @@ -166,6 +166,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,9 @@ #endif #ifdef HAS_MYSQL DICT_TYPE_MYSQL, dict_mysql_open, +#endif +#ifdef HAS_PGSQL + DICT_TYPE_PGSQL, dict_pgsql_open, #endif #ifdef HAS_PCRE DICT_TYPE_PCRE, dict_pcre_open, diff -ruN snapshot-20011210-vanilla/src/util/dict_pgsql.c snapshot-20011210/src/util/dict_pgsql.c --- snapshot-20011210-vanilla/src/util/dict_pgsql.c Thu Jan 1 01:00:00 1970 +++ snapshot-20011210/src/util/dict_pgsql.c Wed Dec 19 10:27:49 2001 @@ -0,0 +1,675 @@ + +/*++ +/* NAME +/* dict_pgsql 3 +/* SUMMARY +/* dictionary manager interface to db files +/* SYNOPSIS +/* #include +/* #include +/* +/* DICT *dict_pgsql_open(name, dummy, unused_dict_flags) +/* const char *name; +/* int dummy; +/* int unused_dict_flags; +/* DESCRIPTION +/* dict_pgsql_open() creates a dictionary of type 'pg'. This +/* dictionary is an interface for the postfix key->value mappings +/* to pgsql. The result is a pointer to the installed dictionary, +/* or a null pointer in case of problems. +/* +/* The pgsql dictionary can manage multiple connections to different +/* sql servers on different hosts. It assumes that the underlying data +/* on each host is identical (mirrored) and maintains one connection +/* at any given time. If any connection fails, any other available +/* ones will be opened and used. The intent of this feature is to eliminate +/* a single point of failure for mail systems that would otherwise rely +/* on a single pgsql server. +/* +/* Arguments: +/* .IP name +/* The path of the PostgreSQL configuration file. The file encodes a number of +/* pieces of information: username, password, databasename, table, +/* select_field, where_field, and hosts. For example, if you want the map to +/* reference databases of the name "your_db" and execute a query like this: +/* select forw_addr from aliases where alias like '' against +/* any database called "vmailer_info" located on hosts host1.some.domain and +/* host2.some.domain, logging in as user "vmailer" and password "passwd" then +/* the configuration file should read: +/* +/* user = vmailer +/* password = passwd +/* DBname = vmailer_info +/* table = aliases +/* select_field = forw_addr +/* where_field = alias +/* hosts = host1.some.domain host2.some.domain +/* +/* .IP other_name +/* reference for outside use. +/* .IP unusued_flags +/* unused flags +/* SEE ALSO +/* dict(3) generic dictionary manager +/* AUTHOR(S) +/* Mathieu Arnold +/* Absolight +/* mat@absolight.com +/* +/* based on dict_mysql by +/* +/* Scott Cotton +/* IC Group, Inc. +/* scott@icgroup.com +/* +/* Joshua Marcus +/* IC Group, Inc. +/* josh@icgroup.com +/*--*/ + +/* System library. */ +#include "sys_defs.h" + +#ifdef HAS_PGSQL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Utility library. */ +#include "dict.h" +#include "msg.h" +#include "mymalloc.h" +#include "dict_pgsql.h" +#include "argv.h" +#include "vstring.h" +#include "split_at.h" +#include "find_inet.h" + +/* need some structs to help organize things */ +typedef struct +{ + PGconn *db; + char *hostname; + int stat; /* STATUNTRIED | STATFAIL | STATCUR */ + time_t ts; /* used for attempting reconnection + * every so often if a host is down */ +} HOST; + +typedef struct +{ + int len_hosts; /* number of hosts */ + HOST *db_hosts; /* the hosts on which the databases + * reside */ +} PLPGSQL; + +typedef struct +{ + char *username; + char *password; + char *dbname; + char *table; + char *select_field; + char *where_field; + char *additional_conditions; + char **hostnames; + int len_hosts; +} PGSQL_NAME; + +typedef struct +{ + DICT dict; + PLPGSQL *pldb; + PGSQL_NAME *name; +} DICT_PGSQL; + +#define STATACTIVE 0 +#define STATFAIL 1 +#define STATUNTRIED 2 +#define RETRY_CONN_INTV 60 /* 1 minute */ + +/* internal function declarations */ +static PLPGSQL *plpgsql_init (char *hostnames[], int); +static PGresult *plpgsql_query (PLPGSQL *, const char *, char *, char *, + + char *); +static void plpgsql_dealloc (PLPGSQL *); +static void plpgsql_down_host (HOST *); +static void plpgsql_connect_single (HOST *, char *, char *, char *); +static int plpgsql_ready_reconn (HOST *); +static const char *dict_pgsql_lookup (DICT *, const char *); +DICT *dict_pgsql_open (const char *, int, int); +static void dict_pgsql_close (DICT *); +static PGSQL_NAME *pgsqlname_parse (const char *); +static HOST host_init (char *); +void pgsql_escape_string (char *escaped, const char *name, int len); + + + +/********************************************************************** + * public interface dict_pgsql_lookup + * find database entry return 0 if no alias found, set dict_errno + * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success + *********************************************************************/ +static const char *dict_pgsql_lookup (DICT *dict, const char *name) +{ + PGresult *query_res; + char *field; + DICT_PGSQL *dict_pgsql; + PLPGSQL *pldb; + static VSTRING *result; + static VSTRING *query = 0; + int i, + j, + numrows; + char *name_escaped = 0; + + dict_pgsql = (DICT_PGSQL *) dict; + pldb = dict_pgsql->pldb; + /* initialization for query */ + query = vstring_alloc (24); + vstring_strcpy (query, ""); + if ( + (name_escaped = + (char *) mymalloc ((sizeof (char) * (strlen (name) * 2) + 1))) == + NULL) + { + msg_fatal ("dict_pgsql_lookup: out of memory."); + } + /* prepare the query */ + pgsql_escape_string (name_escaped, name, (unsigned int) strlen (name)); + vstring_sprintf (query, "select %s from %s where %s = '%s' %s", + dict_pgsql->name->select_field, dict_pgsql->name->table, + dict_pgsql->name->where_field, name_escaped, + dict_pgsql->name->additional_conditions); + if (msg_verbose) + msg_info ("dict_pgsql_lookup using sql query: %s", + vstring_str (query)); + /* free mem associated with preparing the query */ + myfree (name_escaped); + /* do the query - set dict_errno & cleanup if there's an error */ + if ((query_res = plpgsql_query (pldb, + vstring_str (query), + dict_pgsql->name->dbname, + dict_pgsql->name->username, + dict_pgsql->name->password)) == 0) + { + dict_errno = DICT_ERR_RETRY; + vstring_free (query); + return 0; + } + dict_errno = 0; + /* free the vstring query */ + vstring_free (query); + numrows = PQntuples (query_res); + if (msg_verbose) + msg_info ("dict_pgsql_lookup: retrieved %d rows", numrows); + if (numrows == 0) + { + PQclear (query_res); + return 0; + } + if (result == 0) + result = vstring_alloc (10); + vstring_strcpy (result, ""); + for (i = 0; i < numrows; i++) + { + if (i > 0) + vstring_strcat (result, ","); + for (j = 0; j < PQnfields (query_res); j++) + { + if (j > 0) + vstring_strcat (result, ","); + field = PQgetvalue (query_res, i, j); + vstring_strcat (result, field); + if (msg_verbose > 1) + msg_info ("dict_pgsql_lookup: retrieved field: %d: %s", j, + field); + } + } + PQclear (query_res); + return vstring_str (result); +} + +/* + * plpgsql_query - process a PGSQL query. Return PGresult* on success. + * On failure, log failure and try other db instances. + * on failure of all db instances, return 0; + * close unnecessary active connections + */ + +static PGresult *plpgsql_query (PLPGSQL * PLDB, + const char *query, + char *dbname, char *username, char *password) +{ + int i; + HOST *host; + PGresult *res = 0; + ExecStatusType status; + + for (i = 0; i < PLDB->len_hosts; i++) + { + /* can't deal with typing or reading PLDB->db_hosts[i] over & over */ + host = &(PLDB->db_hosts[i]); + if (msg_verbose > 1) + msg_info ("dict_pgsql: trying host %s stat %d, last res %p", + host->hostname, host->stat, res); + + /* answer already found */ + if (res != 0 && host->stat == STATACTIVE) + { + if (msg_verbose) + msg_info + ("dict_pgsql: closing unnessary connection to %s", + host->hostname); + plpgsql_down_host (host); + } + /* try to connect for the first time if we don't have a result yet */ + if (res == 0 && host->stat == STATUNTRIED) + { + if (msg_verbose) + msg_info ("dict_pgsql: attempting to connect to host %s", + host->hostname); + plpgsql_connect_single (host, dbname, username, password); + } + + /* + * try to reconnect if we don't have an answer and the host had a + * prob in the past and it's time for it to reconnect + */ + if (res == 0 && host->stat == STATFAIL + && host->ts < time ((time_t *) 0)) + { + if (msg_verbose) + msg_info + ("dict_pgsql: attempting to reconnect to host %s", + host->hostname); + plpgsql_connect_single (host, dbname, username, password); + } + + /* + * if we don't have a result and the current host is marked active, + * try the query. If the query fails, mark the host STATFAIL + */ + if (res == 0 && host->stat == STATACTIVE) + { + res = PQexec (host->db, query); + status = PQresultStatus (res); + if (res + && (status = PGRES_COMMAND_OK + || status == PGRES_TUPLES_OK)) + { + if (msg_verbose) + msg_info + ("dict_pgsql: successful query from host %s", + host->hostname); + } + else + { + msg_warn ("%s", PQerrorMessage (host->db)); + plpgsql_down_host (host); + } + } + } + return res; +} + +/* + * plpgsql_connect_single - + * used to reconnect to a single database when one is down or none is + * connected yet. Log all errors and set the stat field of host accordingly + */ +static void +plpgsql_connect_single (HOST *host, char *dbname, char *username, + char *password) +{ + char *destination = host->hostname; + char *hostname = 0; + char *service; + VSTRING *conninfo = vstring_alloc (100); + char *conn; + unsigned port = 0; + + /* + * Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where + * both "inet:" and ":port" are optional. + */ + if (strncmp (destination, "unix:", 5) == 0) + { + vstring_sprintf_append (conninfo, "host=%s ", destination + 5); + } + else + { + if (strncmp (destination, "inet:", 5) == 0) + destination += 5; + hostname = mystrdup (destination); + if ((service = split_at (hostname, ':')) != 0) + { + port = ntohs (find_inet_port (service, "tcp")); + vstring_sprintf_append (conninfo, "host='%s' port='%d'", + hostname, port); + } + else + { + vstring_sprintf_append (conninfo, "host='%s'", hostname); + } + } + + vstring_sprintf_append (conninfo, " dbname='%s' user='%s' password='%s'", dbname, + username, password); + conn = vstring_export (conninfo); + + host->db = PQconnectdb (conn); + + if ((host->db != NULL) && (PQstatus (host->db) == CONNECTION_OK)) + { + if (msg_verbose) + msg_info ("dict_pgsql: successful connection to host %s", + host->hostname); + host->stat = STATACTIVE; + } + else + { + msg_warn ("%s", PQerrorMessage (host->db)); + plpgsql_down_host (host); + } + if (hostname) + myfree (hostname); + myfree (conn); +} + +/* + * plpgsql_down_host - mark a HOST down update ts if marked down + * for the first time so that we'll know when to retry the connection + */ +static void plpgsql_down_host (HOST *host) +{ + if (host->stat != STATFAIL) + { + host->ts = time ((time_t *) 0) + RETRY_CONN_INTV; + host->stat = STATFAIL; + } + PQfinish (host->db); + host->db = 0; +} + +/********************************************************************** + * public interface dict_pgsql_open + * create association with database with appropriate values + * parse the map's config file + * allocate memory + **********************************************************************/ +DICT *dict_pgsql_open (const char *name, int unused_open_flags, + int dict_flags) +{ + DICT_PGSQL *dict_pgsql; + int connections; + + dict_pgsql = (DICT_PGSQL *) dict_alloc (DICT_TYPE_PGSQL, name, + sizeof (DICT_PGSQL)); + dict_pgsql->dict.lookup = dict_pgsql_lookup; + dict_pgsql->dict.close = dict_pgsql_close; + dict_pgsql->dict.flags = dict_flags | DICT_FLAG_FIXED; + dict_pgsql->name = pgsqlname_parse (name); + dict_pgsql->pldb = plpgsql_init (dict_pgsql->name->hostnames, + dict_pgsql->name->len_hosts); + if (dict_pgsql->pldb == NULL) + msg_fatal ("couldn't intialize pldb!\n"); + dict_register (name, (DICT *) dict_pgsql); + return (DICT_DEBUG (&dict_pgsql->dict)); +} + +/* pgsqlname_parse - parse pgsql configuration file */ +static PGSQL_NAME *pgsqlname_parse (const char *pgsqlcf_path) +{ + int i; + char *nameval; + char *hosts; + PGSQL_NAME *name = (PGSQL_NAME *) mymalloc (sizeof (PGSQL_NAME)); + ARGV *hosts_argv; + VSTRING *opt_dict_name; + + /* + * setup a dict containing info in the pgsql cf file. the dict has a + * name, and a path. The name must be distinct from the path, or the + * dict interface gets confused. The name must be distinct for two + * different paths, or the configuration info will cache across different + * pgsql maps, which can be confusing. + */ + opt_dict_name = vstring_alloc (64); + vstring_sprintf (opt_dict_name, "pgsql opt dict %s", pgsqlcf_path); + dict_load_file (vstring_str (opt_dict_name), pgsqlcf_path); + /* pgsql username lookup */ + if ( + (nameval = + (char *) dict_lookup (vstring_str (opt_dict_name), "user")) == NULL) + name->username = mystrdup (""); + else + name->username = mystrdup (nameval); + if (msg_verbose) + msg_info ("pgsqlname_parse(): set username to '%s'", name->username); + /* password lookup */ + if ( + (nameval = + (char *) dict_lookup (vstring_str (opt_dict_name), + "password")) == NULL) + name->password = mystrdup (""); + else + name->password = mystrdup (nameval); + if (msg_verbose) + msg_info ("pgsqlname_parse(): set password to '%s'", name->password); + + /* database name lookup */ + if ( + (nameval = + (char *) dict_lookup (vstring_str (opt_dict_name), + "dbname")) == NULL) + + + + msg_fatal ("%s: pgsql options file does not include database name", + pgsqlcf_path); + else + name->dbname = mystrdup (nameval); + if (msg_verbose) + msg_info ("pgsqlname_parse(): set database name to '%s'", + name->dbname); + + /* table lookup */ + if ( + (nameval = + (char *) dict_lookup (vstring_str (opt_dict_name), "table")) == NULL) + msg_fatal ("%s: pgsql options file does not include table name", + pgsqlcf_path); + else + name->table = mystrdup (nameval); + if (msg_verbose) + msg_info ("pgsqlname_parse(): set table name to '%s'", name->table); + + /* select field lookup */ + if ( + (nameval = + (char *) dict_lookup (vstring_str (opt_dict_name), + "select_field")) == NULL) + + + + msg_fatal ("%s: pgsql options file does not include select field", + pgsqlcf_path); + else + name->select_field = mystrdup (nameval); + if (msg_verbose) + msg_info ("pgsqlname_parse(): set select_field to '%s'", + name->select_field); + + /* where field lookup */ + if ( + (nameval = + (char *) dict_lookup (vstring_str (opt_dict_name), + "where_field")) == NULL) + msg_fatal ("%s: pgsql options file does not include where field", + pgsqlcf_path); + else + name->where_field = mystrdup (nameval); + if (msg_verbose) + msg_info ("pgsqlname_parse(): set where_field to '%s'", + name->where_field); + + /* additional conditions */ + if ( + (nameval = + (char *) dict_lookup (vstring_str (opt_dict_name), + "additional_conditions")) == NULL) + name->additional_conditions = mystrdup (""); + else + name->additional_conditions = mystrdup (nameval); + if (msg_verbose) + msg_info ("pgsqlname_parse(): set additional_conditions to '%s'", + name->additional_conditions); + + /* pgsql server hosts */ + if ( + (nameval = + (char *) dict_lookup (vstring_str (opt_dict_name), "hosts")) == NULL) + hosts = mystrdup (""); + else + hosts = mystrdup (nameval); + /* coo argv interface */ + hosts_argv = argv_split (hosts, " ,\t\r\n"); + + if (hosts_argv->argc == 0) + { /* no hosts specified, + * default to 'localhost' */ + if (msg_verbose) + msg_info + ("pgsqlname_parse(): no hostnames specified, defaulting to 'localhost'"); + argv_add (hosts_argv, "localhost", ARGV_END); + argv_terminate (hosts_argv); + } + name->len_hosts = hosts_argv->argc; + name->hostnames = + + (char **) mymalloc ((sizeof (char *)) * name->len_hosts); + i = 0; + for (i = 0; hosts_argv->argv[i] != NULL; i++) + { + name->hostnames[i] = mystrdup (hosts_argv->argv[i]); + if (msg_verbose) + msg_info + ("pgsqlname_parse(): adding host '%s' to list of pgsql server hosts", + name->hostnames[i]); + } + myfree (hosts); + vstring_free (opt_dict_name); + argv_free (hosts_argv); + return name; +} + + +/* + * plpgsql_init - initalize a PGSQL database. + * Return NULL on failure, or a PLPGSQL * on success. + */ +static PLPGSQL *plpgsql_init (char *hostnames[], int len_hosts) +{ + PLPGSQL *PLDB; + int i; + HOST host; + + if ((PLDB = (PLPGSQL *) mymalloc (sizeof (PLPGSQL))) == NULL) + { + msg_fatal ("mymalloc of pldb failed"); + } + PLDB->len_hosts = len_hosts; + if ((PLDB->db_hosts = (HOST *) mymalloc (sizeof (HOST) * len_hosts)) + == NULL) + return NULL; + + for (i = 0; i < len_hosts; i++) + { + PLDB->db_hosts[i] = host_init (hostnames[i]); + } + return PLDB; +} + + +/* host_init - initialize HOST structure */ +static HOST host_init (char *hostname) +{ + HOST host; + + host.stat = STATUNTRIED; + host.hostname = mystrdup (hostname); + host.db = 0; + host.ts = 0; + return host; +} + +/********************************************************************** + * public interface dict_pgsql_close + * unregister, disassociate from database, freeing appropriate memory + **********************************************************************/ +static void dict_pgsql_close (DICT *dict) +{ + int i; + DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict; + + plpgsql_dealloc (dict_pgsql->pldb); + myfree (dict_pgsql->name->username); + myfree (dict_pgsql->name->password); + myfree (dict_pgsql->name->dbname); + myfree (dict_pgsql->name->table); + myfree (dict_pgsql->name->select_field); + myfree (dict_pgsql->name->where_field); + myfree (dict_pgsql->name->additional_conditions); + for (i = 0; i < dict_pgsql->name->len_hosts; i++) + { + myfree (dict_pgsql->name->hostnames[i]); + } + myfree ((char *) dict_pgsql->name->hostnames); + myfree ((char *) dict_pgsql->name); + dict_free (dict); +} + +/* plpgsql_dealloc - free memory associated with PLPGSQL close databases */ +static void plpgsql_dealloc (PLPGSQL * PLDB) +{ + int i; + + for (i = 0; i < PLDB->len_hosts; i++) + { + if (PLDB->db_hosts[i].db) + PQfinish (PLDB->db_hosts[i].db); + myfree (PLDB->db_hosts[i].hostname); + } + myfree ((char *) PLDB->db_hosts); + myfree ((char *) (PLDB)); +} + +/* pgsql_escape_string - replace mysql_escape_string */ +void pgsql_escape_string (char *escaped, const char *name, int len) +{ + int i, + j; + + for (i = 0, j = 0; i <= len; i++, j++) + { + if ((name[i] == '\'') || (name[i] == '\\')) + { + escaped[j] = '\\'; + j++; + } + escaped[j] = name[i]; + } +} + + + +#endif + diff -ruN snapshot-20011210-vanilla/src/util/dict_pgsql.h snapshot-20011210/src/util/dict_pgsql.h --- snapshot-20011210-vanilla/src/util/dict_pgsql.h Thu Jan 1 01:00:00 1970 +++ snapshot-20011210/src/util/dict_pgsql.h Wed Dec 19 10:27:49 2001 @@ -0,0 +1,41 @@ +#ifndef _DICT_PGSQL_H_INCLUDED_ +#define _DICT_PGSQL_H_INCLUDED_ + +/*++ +/* NAME +/* dict_pgsql 3h +/* SUMMARY +/* dictionary manager interface to pgsql databases +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +#define DICT_TYPE_PGSQL "pgsql" + +extern DICT *dict_pgsql_open (const char *, int, int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Scott Cotton +/* IC Group, Inc. +/* scott@icgroup.com +/* +/* Joshua Marcus +/* IC Group, Inc. +/* josh@icgroup.com +/*--*/ + +#endif +