]>
Commit | Line | Data |
---|---|---|
0b72d562 | 1 | --- postfix-2.0.16.orig/PGSQL_README 1970-01-01 01:00:00.000000000 +0100 |
2 | +++ postfix-2.0.16/PGSQL_README 2003-11-08 20:11:44.000000000 +0100 | |
f512ae96 JB |
3 | @@ -0,0 +1,88 @@ |
4 | +[Code contributed by Mathieu Arnold] | |
5 | + | |
6 | +We've written code to add a pgsql map type. It utilizes the pgsql | |
7 | +client library, which can be obtained from: | |
8 | + | |
9 | + http://www.postgresql.org/ | |
10 | + | |
11 | +In order to build postfix with pgsql map support, you will need to add | |
12 | +-DHAS_PGSQL and -I for the directory containing the postgres headers, and | |
13 | +the libpq library (and libcrypt) to AUXLIBS, for example: | |
14 | + | |
15 | +make -f Makefile.init makefiles \ | |
16 | + 'CCARGS=-DHAS_PGSQL -I/some/where/include/postgresql' \ | |
17 | + 'AUXLIBS=/some/where/lib/postgres/libpq.a -lcrypt' | |
18 | + | |
19 | +then, just run 'make'. | |
20 | + | |
21 | +Postfix installations which may benefit from using pgsql map types | |
22 | +include sites that have a need for instantaneous updates of | |
23 | +forwarding, and sites that may benefit from having mail exchangers | |
24 | +reference a networked database, possibly working in conjunction with a | |
25 | +customer database of sorts. | |
26 | + | |
27 | +Once postfix is built with pgsql support, you can specify a map type | |
28 | +in main.cf like this: | |
29 | + | |
30 | +alias_maps = pgsql:/etc/postfix/pgsql-aliases.cf | |
31 | + | |
32 | +The file /etc/postfix/pgsql-aliases.cf specifies lots of information | |
33 | +telling postfix how to reference the postgresql database. An example | |
34 | +postgresql map config file follows: | |
35 | + | |
36 | +# | |
37 | +# postgresql config file for alias lookups on postfix | |
38 | +# comments are ok. | |
39 | +# | |
40 | + | |
41 | +# the user name and password to log into the pgsql server | |
42 | +user = someone | |
43 | +password = some_passwordd | |
44 | + | |
45 | +# the database name on the servers | |
46 | +dbname = customer_database | |
47 | + | |
48 | +# the table name | |
49 | +table = mxaliases | |
50 | + | |
51 | +# | |
52 | +select_field = forw_addr | |
53 | +where_field = alias | |
54 | + | |
55 | +# you may specify additional_conditions here | |
56 | +additional_conditions = and status = 'paid' | |
57 | + | |
58 | +# the above variables will result in a query of | |
59 | +# the form: | |
60 | +# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid' | |
61 | +# ($lookup is escaped so if it contains single quotes or other odd | |
62 | +# characters, it will not cause a parse error in the sql). | |
63 | +# | |
64 | +# the hosts that postfix will try to connect to | |
65 | +# and query from (in the order listed) | |
66 | +hosts = host1.some.domain host2.some.domain | |
67 | + | |
68 | +# end postgresql config file | |
69 | + | |
70 | +Some notes: | |
71 | + | |
72 | +This configuration interface setup allows for multiple postgresql | |
73 | +databases: you can use one for a virtual table, one for an access | |
74 | +table, and one for an aliases table if you want. | |
75 | + | |
76 | +Since sites that have a need for multiple mail exchangers may enjoy | |
77 | +the convenience of using a networked mailer database, but do not want | |
78 | +to introduce a single point of failure to their system, we've included | |
79 | +the ability to have postfix reference multiple hosts for access to a | |
80 | +single pgsql map. This will work if sites set up mirrored pgsql | |
81 | +databases on two or more hosts. Whenever queries fail with an error | |
82 | +at one host, the rest of the hosts will be tried in order. Each host | |
83 | +that is in an error state will undergo a reconnection attempt every so | |
84 | +often, and if no pgsql server hosts are reachable, then mail will be | |
85 | +deferred until atleast one of those hosts is reachable. | |
86 | + | |
87 | +Performance of postfix with pgsql has not been thoroughly tested, | |
88 | +however, we have found it to be stable. Busy mail servers using pgsql | |
89 | +maps will generate lots of concurrent pgsql clients, so the pgsql | |
90 | +server(s) should be run with this fact in mind. Any further | |
91 | +performance information, in addition to any feedback is most welcome. | |
0b72d562 | 92 | --- postfix-2.0.16.orig/src/util/dict_open.c 2003-11-08 20:10:08.000000000 +0100 |
93 | +++ postfix-2.0.16/src/util/dict_open.c 2003-11-08 20:11:44.000000000 +0100 | |
94 | @@ -178,6 +178,7 @@ | |
f512ae96 JB |
95 | #include <dict_ni.h> |
96 | #include <dict_ldap.h> | |
97 | #include <dict_mysql.h> | |
98 | +#include <dict_pgsql.h> | |
99 | #include <dict_pcre.h> | |
100 | #include <dict_regexp.h> | |
101 | #include <dict_static.h> | |
0b72d562 | 102 | @@ -231,6 +232,9 @@ |
f512ae96 JB |
103 | #ifdef HAS_MYSQL |
104 | DICT_TYPE_MYSQL, dict_mysql_open, | |
105 | #endif | |
106 | +#ifdef HAS_PGSQL | |
107 | + DICT_TYPE_PGSQL, dict_pgsql_open, | |
108 | +#endif | |
109 | #ifdef HAS_PCRE | |
110 | DICT_TYPE_PCRE, dict_pcre_open, | |
111 | #endif | |
0b72d562 | 112 | --- postfix-2.0.16.orig/src/util/dict_pgsql.c 1970-01-01 01:00:00.000000000 +0100 |
113 | +++ postfix-2.0.16/src/util/dict_pgsql.c 2003-11-08 20:11:44.000000000 +0100 | |
f512ae96 JB |
114 | @@ -0,0 +1,675 @@ |
115 | + | |
116 | +/*++ | |
117 | +/* NAME | |
118 | +/* dict_pgsql 3 | |
119 | +/* SUMMARY | |
120 | +/* dictionary manager interface to db files | |
121 | +/* SYNOPSIS | |
122 | +/* #include <dict.h> | |
123 | +/* #include <dict_pgsql.h> | |
124 | +/* | |
125 | +/* DICT *dict_pgsql_open(name, dummy, unused_dict_flags) | |
126 | +/* const char *name; | |
127 | +/* int dummy; | |
128 | +/* int unused_dict_flags; | |
129 | +/* DESCRIPTION | |
130 | +/* dict_pgsql_open() creates a dictionary of type 'pg'. This | |
131 | +/* dictionary is an interface for the postfix key->value mappings | |
132 | +/* to pgsql. The result is a pointer to the installed dictionary, | |
133 | +/* or a null pointer in case of problems. | |
134 | +/* | |
135 | +/* The pgsql dictionary can manage multiple connections to different | |
136 | +/* sql servers on different hosts. It assumes that the underlying data | |
137 | +/* on each host is identical (mirrored) and maintains one connection | |
138 | +/* at any given time. If any connection fails, any other available | |
139 | +/* ones will be opened and used. The intent of this feature is to eliminate | |
140 | +/* a single point of failure for mail systems that would otherwise rely | |
141 | +/* on a single pgsql server. | |
142 | +/* | |
143 | +/* Arguments: | |
144 | +/* .IP name | |
145 | +/* The path of the PostgreSQL configuration file. The file encodes a number of | |
146 | +/* pieces of information: username, password, databasename, table, | |
147 | +/* select_field, where_field, and hosts. For example, if you want the map to | |
148 | +/* reference databases of the name "your_db" and execute a query like this: | |
149 | +/* select forw_addr from aliases where alias like '<some username>' against | |
150 | +/* any database called "vmailer_info" located on hosts host1.some.domain and | |
151 | +/* host2.some.domain, logging in as user "vmailer" and password "passwd" then | |
152 | +/* the configuration file should read: | |
153 | +/* | |
154 | +/* user = vmailer | |
155 | +/* password = passwd | |
156 | +/* DBname = vmailer_info | |
157 | +/* table = aliases | |
158 | +/* select_field = forw_addr | |
159 | +/* where_field = alias | |
160 | +/* hosts = host1.some.domain host2.some.domain | |
161 | +/* | |
162 | +/* .IP other_name | |
163 | +/* reference for outside use. | |
164 | +/* .IP unusued_flags | |
165 | +/* unused flags | |
166 | +/* SEE ALSO | |
167 | +/* dict(3) generic dictionary manager | |
168 | +/* AUTHOR(S) | |
169 | +/* Mathieu Arnold | |
170 | +/* Absolight | |
171 | +/* mat@absolight.com | |
172 | +/* | |
173 | +/* based on dict_mysql by | |
174 | +/* | |
175 | +/* Scott Cotton | |
176 | +/* IC Group, Inc. | |
177 | +/* scott@icgroup.com | |
178 | +/* | |
179 | +/* Joshua Marcus | |
180 | +/* IC Group, Inc. | |
181 | +/* josh@icgroup.com | |
182 | +/*--*/ | |
183 | + | |
184 | +/* System library. */ | |
185 | +#include "sys_defs.h" | |
186 | + | |
187 | +#ifdef HAS_PGSQL | |
188 | +#include <sys/socket.h> | |
189 | +#include <netinet/in.h> | |
190 | +#include <arpa/inet.h> | |
191 | +#include <netdb.h> | |
192 | +#include <stdio.h> | |
193 | +#include <string.h> | |
194 | +#include <stdlib.h> | |
195 | +#include <syslog.h> | |
196 | +#include <time.h> | |
197 | +#include <libpq-fe.h> | |
198 | + | |
199 | +/* Utility library. */ | |
200 | +#include "dict.h" | |
201 | +#include "msg.h" | |
202 | +#include "mymalloc.h" | |
203 | +#include "dict_pgsql.h" | |
204 | +#include "argv.h" | |
205 | +#include "vstring.h" | |
206 | +#include "split_at.h" | |
207 | +#include "find_inet.h" | |
208 | + | |
209 | +/* need some structs to help organize things */ | |
210 | +typedef struct | |
211 | +{ | |
212 | + PGconn *db; | |
213 | + char *hostname; | |
214 | + int stat; /* STATUNTRIED | STATFAIL | STATCUR */ | |
215 | + time_t ts; /* used for attempting reconnection | |
216 | + * every so often if a host is down */ | |
217 | +} HOST; | |
218 | + | |
219 | +typedef struct | |
220 | +{ | |
221 | + int len_hosts; /* number of hosts */ | |
222 | + HOST *db_hosts; /* the hosts on which the databases | |
223 | + * reside */ | |
224 | +} PLPGSQL; | |
225 | + | |
226 | +typedef struct | |
227 | +{ | |
228 | + char *username; | |
229 | + char *password; | |
230 | + char *dbname; | |
231 | + char *table; | |
232 | + char *select_field; | |
233 | + char *where_field; | |
234 | + char *additional_conditions; | |
235 | + char **hostnames; | |
236 | + int len_hosts; | |
237 | +} PGSQL_NAME; | |
238 | + | |
239 | +typedef struct | |
240 | +{ | |
241 | + DICT dict; | |
242 | + PLPGSQL *pldb; | |
243 | + PGSQL_NAME *name; | |
244 | +} DICT_PGSQL; | |
245 | + | |
246 | +#define STATACTIVE 0 | |
247 | +#define STATFAIL 1 | |
248 | +#define STATUNTRIED 2 | |
249 | +#define RETRY_CONN_INTV 60 /* 1 minute */ | |
250 | + | |
251 | +/* internal function declarations */ | |
252 | +static PLPGSQL *plpgsql_init (char *hostnames[], int); | |
253 | +static PGresult *plpgsql_query (PLPGSQL *, const char *, char *, char *, | |
254 | + | |
255 | + char *); | |
256 | +static void plpgsql_dealloc (PLPGSQL *); | |
257 | +static void plpgsql_down_host (HOST *); | |
258 | +static void plpgsql_connect_single (HOST *, char *, char *, char *); | |
259 | +static int plpgsql_ready_reconn (HOST *); | |
260 | +static const char *dict_pgsql_lookup (DICT *, const char *); | |
261 | +DICT *dict_pgsql_open (const char *, int, int); | |
262 | +static void dict_pgsql_close (DICT *); | |
263 | +static PGSQL_NAME *pgsqlname_parse (const char *); | |
264 | +static HOST host_init (char *); | |
265 | +void pgsql_escape_string (char *escaped, const char *name, int len); | |
266 | + | |
267 | + | |
268 | + | |
269 | +/********************************************************************** | |
270 | + * public interface dict_pgsql_lookup | |
271 | + * find database entry return 0 if no alias found, set dict_errno | |
272 | + * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success | |
273 | + *********************************************************************/ | |
274 | +static const char *dict_pgsql_lookup (DICT *dict, const char *name) | |
275 | +{ | |
276 | + PGresult *query_res; | |
277 | + char *field; | |
278 | + DICT_PGSQL *dict_pgsql; | |
279 | + PLPGSQL *pldb; | |
280 | + static VSTRING *result; | |
281 | + static VSTRING *query = 0; | |
282 | + int i, | |
283 | + j, | |
284 | + numrows; | |
285 | + char *name_escaped = 0; | |
286 | + | |
287 | + dict_pgsql = (DICT_PGSQL *) dict; | |
288 | + pldb = dict_pgsql->pldb; | |
289 | + /* initialization for query */ | |
290 | + query = vstring_alloc (24); | |
291 | + vstring_strcpy (query, ""); | |
292 | + if ( | |
293 | + (name_escaped = | |
294 | + (char *) mymalloc ((sizeof (char) * (strlen (name) * 2) + 1))) == | |
295 | + NULL) | |
296 | + { | |
297 | + msg_fatal ("dict_pgsql_lookup: out of memory."); | |
298 | + } | |
299 | + /* prepare the query */ | |
300 | + pgsql_escape_string (name_escaped, name, (unsigned int) strlen (name)); | |
301 | + vstring_sprintf (query, "select %s from %s where %s = '%s' %s", | |
302 | + dict_pgsql->name->select_field, dict_pgsql->name->table, | |
303 | + dict_pgsql->name->where_field, name_escaped, | |
304 | + dict_pgsql->name->additional_conditions); | |
305 | + if (msg_verbose) | |
306 | + msg_info ("dict_pgsql_lookup using sql query: %s", | |
307 | + vstring_str (query)); | |
308 | + /* free mem associated with preparing the query */ | |
309 | + myfree (name_escaped); | |
310 | + /* do the query - set dict_errno & cleanup if there's an error */ | |
311 | + if ((query_res = plpgsql_query (pldb, | |
312 | + vstring_str (query), | |
313 | + dict_pgsql->name->dbname, | |
314 | + dict_pgsql->name->username, | |
315 | + dict_pgsql->name->password)) == 0) | |
316 | + { | |
317 | + dict_errno = DICT_ERR_RETRY; | |
318 | + vstring_free (query); | |
319 | + return 0; | |
320 | + } | |
321 | + dict_errno = 0; | |
322 | + /* free the vstring query */ | |
323 | + vstring_free (query); | |
324 | + numrows = PQntuples (query_res); | |
325 | + if (msg_verbose) | |
326 | + msg_info ("dict_pgsql_lookup: retrieved %d rows", numrows); | |
327 | + if (numrows == 0) | |
328 | + { | |
329 | + PQclear (query_res); | |
330 | + return 0; | |
331 | + } | |
332 | + if (result == 0) | |
333 | + result = vstring_alloc (10); | |
334 | + vstring_strcpy (result, ""); | |
335 | + for (i = 0; i < numrows; i++) | |
336 | + { | |
337 | + if (i > 0) | |
338 | + vstring_strcat (result, ","); | |
339 | + for (j = 0; j < PQnfields (query_res); j++) | |
340 | + { | |
341 | + if (j > 0) | |
342 | + vstring_strcat (result, ","); | |
343 | + field = PQgetvalue (query_res, i, j); | |
344 | + vstring_strcat (result, field); | |
345 | + if (msg_verbose > 1) | |
346 | + msg_info ("dict_pgsql_lookup: retrieved field: %d: %s", j, | |
347 | + field); | |
348 | + } | |
349 | + } | |
350 | + PQclear (query_res); | |
351 | + return vstring_str (result); | |
352 | +} | |
353 | + | |
354 | +/* | |
355 | + * plpgsql_query - process a PGSQL query. Return PGresult* on success. | |
356 | + * On failure, log failure and try other db instances. | |
357 | + * on failure of all db instances, return 0; | |
358 | + * close unnecessary active connections | |
359 | + */ | |
360 | + | |
361 | +static PGresult *plpgsql_query (PLPGSQL * PLDB, | |
362 | + const char *query, | |
363 | + char *dbname, char *username, char *password) | |
364 | +{ | |
365 | + int i; | |
366 | + HOST *host; | |
367 | + PGresult *res = 0; | |
368 | + ExecStatusType status; | |
369 | + | |
370 | + for (i = 0; i < PLDB->len_hosts; i++) | |
371 | + { | |
372 | + /* can't deal with typing or reading PLDB->db_hosts[i] over & over */ | |
373 | + host = &(PLDB->db_hosts[i]); | |
374 | + if (msg_verbose > 1) | |
375 | + msg_info ("dict_pgsql: trying host %s stat %d, last res %p", | |
376 | + host->hostname, host->stat, res); | |
377 | + | |
378 | + /* answer already found */ | |
379 | + if (res != 0 && host->stat == STATACTIVE) | |
380 | + { | |
381 | + if (msg_verbose) | |
382 | + msg_info | |
383 | + ("dict_pgsql: closing unnessary connection to %s", | |
384 | + host->hostname); | |
385 | + plpgsql_down_host (host); | |
386 | + } | |
387 | + /* try to connect for the first time if we don't have a result yet */ | |
388 | + if (res == 0 && host->stat == STATUNTRIED) | |
389 | + { | |
390 | + if (msg_verbose) | |
391 | + msg_info ("dict_pgsql: attempting to connect to host %s", | |
392 | + host->hostname); | |
393 | + plpgsql_connect_single (host, dbname, username, password); | |
394 | + } | |
395 | + | |
396 | + /* | |
397 | + * try to reconnect if we don't have an answer and the host had a | |
398 | + * prob in the past and it's time for it to reconnect | |
399 | + */ | |
400 | + if (res == 0 && host->stat == STATFAIL | |
401 | + && host->ts < time ((time_t *) 0)) | |
402 | + { | |
403 | + if (msg_verbose) | |
404 | + msg_info | |
405 | + ("dict_pgsql: attempting to reconnect to host %s", | |
406 | + host->hostname); | |
407 | + plpgsql_connect_single (host, dbname, username, password); | |
408 | + } | |
409 | + | |
410 | + /* | |
411 | + * if we don't have a result and the current host is marked active, | |
412 | + * try the query. If the query fails, mark the host STATFAIL | |
413 | + */ | |
414 | + if (res == 0 && host->stat == STATACTIVE) | |
415 | + { | |
416 | + res = PQexec (host->db, query); | |
417 | + status = PQresultStatus (res); | |
418 | + if (res | |
419 | + && (status = PGRES_COMMAND_OK | |
420 | + || status == PGRES_TUPLES_OK)) | |
421 | + { | |
422 | + if (msg_verbose) | |
423 | + msg_info | |
424 | + ("dict_pgsql: successful query from host %s", | |
425 | + host->hostname); | |
426 | + } | |
427 | + else | |
428 | + { | |
429 | + msg_warn ("%s", PQerrorMessage (host->db)); | |
430 | + plpgsql_down_host (host); | |
431 | + } | |
432 | + } | |
433 | + } | |
434 | + return res; | |
435 | +} | |
436 | + | |
437 | +/* | |
438 | + * plpgsql_connect_single - | |
439 | + * used to reconnect to a single database when one is down or none is | |
440 | + * connected yet. Log all errors and set the stat field of host accordingly | |
441 | + */ | |
442 | +static void | |
443 | +plpgsql_connect_single (HOST *host, char *dbname, char *username, | |
444 | + char *password) | |
445 | +{ | |
446 | + char *destination = host->hostname; | |
447 | + char *hostname = 0; | |
448 | + char *service; | |
449 | + VSTRING *conninfo = vstring_alloc (100); | |
450 | + char *conn; | |
451 | + unsigned port = 0; | |
452 | + | |
453 | + /* | |
454 | + * Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where | |
455 | + * both "inet:" and ":port" are optional. | |
456 | + */ | |
457 | + if (strncmp (destination, "unix:", 5) == 0) | |
458 | + { | |
459 | + vstring_sprintf_append (conninfo, "host=%s ", destination + 5); | |
460 | + } | |
461 | + else | |
462 | + { | |
463 | + if (strncmp (destination, "inet:", 5) == 0) | |
464 | + destination += 5; | |
465 | + hostname = mystrdup (destination); | |
466 | + if ((service = split_at (hostname, ':')) != 0) | |
467 | + { | |
468 | + port = ntohs (find_inet_port (service, "tcp")); | |
469 | + vstring_sprintf_append (conninfo, "host='%s' port='%d'", | |
470 | + hostname, port); | |
471 | + } | |
472 | + else | |
473 | + { | |
474 | + vstring_sprintf_append (conninfo, "host='%s'", hostname); | |
475 | + } | |
476 | + } | |
477 | + | |
478 | + vstring_sprintf_append (conninfo, " dbname='%s' user='%s' password='%s'", dbname, | |
479 | + username, password); | |
480 | + conn = vstring_export (conninfo); | |
481 | + | |
482 | + host->db = PQconnectdb (conn); | |
483 | + | |
484 | + if ((host->db != NULL) && (PQstatus (host->db) == CONNECTION_OK)) | |
485 | + { | |
486 | + if (msg_verbose) | |
487 | + msg_info ("dict_pgsql: successful connection to host %s", | |
488 | + host->hostname); | |
489 | + host->stat = STATACTIVE; | |
490 | + } | |
491 | + else | |
492 | + { | |
493 | + msg_warn ("%s", PQerrorMessage (host->db)); | |
494 | + plpgsql_down_host (host); | |
495 | + } | |
496 | + if (hostname) | |
497 | + myfree (hostname); | |
498 | + myfree (conn); | |
499 | +} | |
500 | + | |
501 | +/* | |
502 | + * plpgsql_down_host - mark a HOST down update ts if marked down | |
503 | + * for the first time so that we'll know when to retry the connection | |
504 | + */ | |
505 | +static void plpgsql_down_host (HOST *host) | |
506 | +{ | |
507 | + if (host->stat != STATFAIL) | |
508 | + { | |
509 | + host->ts = time ((time_t *) 0) + RETRY_CONN_INTV; | |
510 | + host->stat = STATFAIL; | |
511 | + } | |
512 | + PQfinish (host->db); | |
513 | + host->db = 0; | |
514 | +} | |
515 | + | |
516 | +/********************************************************************** | |
517 | + * public interface dict_pgsql_open | |
518 | + * create association with database with appropriate values | |
519 | + * parse the map's config file | |
520 | + * allocate memory | |
521 | + **********************************************************************/ | |
522 | +DICT *dict_pgsql_open (const char *name, int unused_open_flags, | |
523 | + int dict_flags) | |
524 | +{ | |
525 | + DICT_PGSQL *dict_pgsql; | |
526 | + int connections; | |
527 | + | |
528 | + dict_pgsql = (DICT_PGSQL *) dict_alloc (DICT_TYPE_PGSQL, name, | |
529 | + sizeof (DICT_PGSQL)); | |
530 | + dict_pgsql->dict.lookup = dict_pgsql_lookup; | |
531 | + dict_pgsql->dict.close = dict_pgsql_close; | |
532 | + dict_pgsql->dict.flags = dict_flags | DICT_FLAG_FIXED; | |
533 | + dict_pgsql->name = pgsqlname_parse (name); | |
534 | + dict_pgsql->pldb = plpgsql_init (dict_pgsql->name->hostnames, | |
535 | + dict_pgsql->name->len_hosts); | |
536 | + if (dict_pgsql->pldb == NULL) | |
537 | + msg_fatal ("couldn't intialize pldb!\n"); | |
538 | + dict_register (name, (DICT *) dict_pgsql); | |
539 | + return (DICT_DEBUG (&dict_pgsql->dict)); | |
540 | +} | |
541 | + | |
542 | +/* pgsqlname_parse - parse pgsql configuration file */ | |
543 | +static PGSQL_NAME *pgsqlname_parse (const char *pgsqlcf_path) | |
544 | +{ | |
545 | + int i; | |
546 | + char *nameval; | |
547 | + char *hosts; | |
548 | + PGSQL_NAME *name = (PGSQL_NAME *) mymalloc (sizeof (PGSQL_NAME)); | |
549 | + ARGV *hosts_argv; | |
550 | + VSTRING *opt_dict_name; | |
551 | + | |
552 | + /* | |
553 | + * setup a dict containing info in the pgsql cf file. the dict has a | |
554 | + * name, and a path. The name must be distinct from the path, or the | |
555 | + * dict interface gets confused. The name must be distinct for two | |
556 | + * different paths, or the configuration info will cache across different | |
557 | + * pgsql maps, which can be confusing. | |
558 | + */ | |
559 | + opt_dict_name = vstring_alloc (64); | |
560 | + vstring_sprintf (opt_dict_name, "pgsql opt dict %s", pgsqlcf_path); | |
561 | + dict_load_file (vstring_str (opt_dict_name), pgsqlcf_path); | |
562 | + /* pgsql username lookup */ | |
563 | + if ( | |
564 | + (nameval = | |
565 | + (char *) dict_lookup (vstring_str (opt_dict_name), "user")) == NULL) | |
566 | + name->username = mystrdup (""); | |
567 | + else | |
568 | + name->username = mystrdup (nameval); | |
569 | + if (msg_verbose) | |
570 | + msg_info ("pgsqlname_parse(): set username to '%s'", name->username); | |
571 | + /* password lookup */ | |
572 | + if ( | |
573 | + (nameval = | |
574 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
575 | + "password")) == NULL) | |
576 | + name->password = mystrdup (""); | |
577 | + else | |
578 | + name->password = mystrdup (nameval); | |
579 | + if (msg_verbose) | |
580 | + msg_info ("pgsqlname_parse(): set password to '%s'", name->password); | |
581 | + | |
582 | + /* database name lookup */ | |
583 | + if ( | |
584 | + (nameval = | |
585 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
586 | + "dbname")) == NULL) | |
587 | + | |
588 | + | |
589 | + | |
590 | + msg_fatal ("%s: pgsql options file does not include database name", | |
591 | + pgsqlcf_path); | |
592 | + else | |
593 | + name->dbname = mystrdup (nameval); | |
594 | + if (msg_verbose) | |
595 | + msg_info ("pgsqlname_parse(): set database name to '%s'", | |
596 | + name->dbname); | |
597 | + | |
598 | + /* table lookup */ | |
599 | + if ( | |
600 | + (nameval = | |
601 | + (char *) dict_lookup (vstring_str (opt_dict_name), "table")) == NULL) | |
602 | + msg_fatal ("%s: pgsql options file does not include table name", | |
603 | + pgsqlcf_path); | |
604 | + else | |
605 | + name->table = mystrdup (nameval); | |
606 | + if (msg_verbose) | |
607 | + msg_info ("pgsqlname_parse(): set table name to '%s'", name->table); | |
608 | + | |
609 | + /* select field lookup */ | |
610 | + if ( | |
611 | + (nameval = | |
612 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
613 | + "select_field")) == NULL) | |
614 | + | |
615 | + | |
616 | + | |
617 | + msg_fatal ("%s: pgsql options file does not include select field", | |
618 | + pgsqlcf_path); | |
619 | + else | |
620 | + name->select_field = mystrdup (nameval); | |
621 | + if (msg_verbose) | |
622 | + msg_info ("pgsqlname_parse(): set select_field to '%s'", | |
623 | + name->select_field); | |
624 | + | |
625 | + /* where field lookup */ | |
626 | + if ( | |
627 | + (nameval = | |
628 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
629 | + "where_field")) == NULL) | |
630 | + msg_fatal ("%s: pgsql options file does not include where field", | |
631 | + pgsqlcf_path); | |
632 | + else | |
633 | + name->where_field = mystrdup (nameval); | |
634 | + if (msg_verbose) | |
635 | + msg_info ("pgsqlname_parse(): set where_field to '%s'", | |
636 | + name->where_field); | |
637 | + | |
638 | + /* additional conditions */ | |
639 | + if ( | |
640 | + (nameval = | |
641 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
642 | + "additional_conditions")) == NULL) | |
643 | + name->additional_conditions = mystrdup (""); | |
644 | + else | |
645 | + name->additional_conditions = mystrdup (nameval); | |
646 | + if (msg_verbose) | |
647 | + msg_info ("pgsqlname_parse(): set additional_conditions to '%s'", | |
648 | + name->additional_conditions); | |
649 | + | |
650 | + /* pgsql server hosts */ | |
651 | + if ( | |
652 | + (nameval = | |
653 | + (char *) dict_lookup (vstring_str (opt_dict_name), "hosts")) == NULL) | |
654 | + hosts = mystrdup (""); | |
655 | + else | |
656 | + hosts = mystrdup (nameval); | |
657 | + /* coo argv interface */ | |
658 | + hosts_argv = argv_split (hosts, " ,\t\r\n"); | |
659 | + | |
660 | + if (hosts_argv->argc == 0) | |
661 | + { /* no hosts specified, | |
662 | + * default to 'localhost' */ | |
663 | + if (msg_verbose) | |
664 | + msg_info | |
665 | + ("pgsqlname_parse(): no hostnames specified, defaulting to 'localhost'"); | |
666 | + argv_add (hosts_argv, "localhost", ARGV_END); | |
667 | + argv_terminate (hosts_argv); | |
668 | + } | |
669 | + name->len_hosts = hosts_argv->argc; | |
670 | + name->hostnames = | |
671 | + | |
672 | + (char **) mymalloc ((sizeof (char *)) * name->len_hosts); | |
673 | + i = 0; | |
674 | + for (i = 0; hosts_argv->argv[i] != NULL; i++) | |
675 | + { | |
676 | + name->hostnames[i] = mystrdup (hosts_argv->argv[i]); | |
677 | + if (msg_verbose) | |
678 | + msg_info | |
679 | + ("pgsqlname_parse(): adding host '%s' to list of pgsql server hosts", | |
680 | + name->hostnames[i]); | |
681 | + } | |
682 | + myfree (hosts); | |
683 | + vstring_free (opt_dict_name); | |
684 | + argv_free (hosts_argv); | |
685 | + return name; | |
686 | +} | |
687 | + | |
688 | + | |
689 | +/* | |
690 | + * plpgsql_init - initalize a PGSQL database. | |
691 | + * Return NULL on failure, or a PLPGSQL * on success. | |
692 | + */ | |
693 | +static PLPGSQL *plpgsql_init (char *hostnames[], int len_hosts) | |
694 | +{ | |
695 | + PLPGSQL *PLDB; | |
696 | + int i; | |
697 | + HOST host; | |
698 | + | |
699 | + if ((PLDB = (PLPGSQL *) mymalloc (sizeof (PLPGSQL))) == NULL) | |
700 | + { | |
701 | + msg_fatal ("mymalloc of pldb failed"); | |
702 | + } | |
703 | + PLDB->len_hosts = len_hosts; | |
704 | + if ((PLDB->db_hosts = (HOST *) mymalloc (sizeof (HOST) * len_hosts)) | |
705 | + == NULL) | |
706 | + return NULL; | |
707 | + | |
708 | + for (i = 0; i < len_hosts; i++) | |
709 | + { | |
710 | + PLDB->db_hosts[i] = host_init (hostnames[i]); | |
711 | + } | |
712 | + return PLDB; | |
713 | +} | |
714 | + | |
715 | + | |
716 | +/* host_init - initialize HOST structure */ | |
717 | +static HOST host_init (char *hostname) | |
718 | +{ | |
719 | + HOST host; | |
720 | + | |
721 | + host.stat = STATUNTRIED; | |
722 | + host.hostname = mystrdup (hostname); | |
723 | + host.db = 0; | |
724 | + host.ts = 0; | |
725 | + return host; | |
726 | +} | |
727 | + | |
728 | +/********************************************************************** | |
729 | + * public interface dict_pgsql_close | |
730 | + * unregister, disassociate from database, freeing appropriate memory | |
731 | + **********************************************************************/ | |
732 | +static void dict_pgsql_close (DICT *dict) | |
733 | +{ | |
734 | + int i; | |
735 | + DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict; | |
736 | + | |
737 | + plpgsql_dealloc (dict_pgsql->pldb); | |
738 | + myfree (dict_pgsql->name->username); | |
739 | + myfree (dict_pgsql->name->password); | |
740 | + myfree (dict_pgsql->name->dbname); | |
741 | + myfree (dict_pgsql->name->table); | |
742 | + myfree (dict_pgsql->name->select_field); | |
743 | + myfree (dict_pgsql->name->where_field); | |
744 | + myfree (dict_pgsql->name->additional_conditions); | |
745 | + for (i = 0; i < dict_pgsql->name->len_hosts; i++) | |
746 | + { | |
747 | + myfree (dict_pgsql->name->hostnames[i]); | |
748 | + } | |
749 | + myfree ((char *) dict_pgsql->name->hostnames); | |
750 | + myfree ((char *) dict_pgsql->name); | |
751 | + dict_free (dict); | |
752 | +} | |
753 | + | |
754 | +/* plpgsql_dealloc - free memory associated with PLPGSQL close databases */ | |
755 | +static void plpgsql_dealloc (PLPGSQL * PLDB) | |
756 | +{ | |
757 | + int i; | |
758 | + | |
759 | + for (i = 0; i < PLDB->len_hosts; i++) | |
760 | + { | |
761 | + if (PLDB->db_hosts[i].db) | |
762 | + PQfinish (PLDB->db_hosts[i].db); | |
763 | + myfree (PLDB->db_hosts[i].hostname); | |
764 | + } | |
765 | + myfree ((char *) PLDB->db_hosts); | |
766 | + myfree ((char *) (PLDB)); | |
767 | +} | |
768 | + | |
769 | +/* pgsql_escape_string - replace mysql_escape_string */ | |
770 | +void pgsql_escape_string (char *escaped, const char *name, int len) | |
771 | +{ | |
772 | + int i, | |
773 | + j; | |
774 | + | |
775 | + for (i = 0, j = 0; i <= len; i++, j++) | |
776 | + { | |
777 | + if ((name[i] == '\'') || (name[i] == '\\')) | |
778 | + { | |
779 | + escaped[j] = '\\'; | |
780 | + j++; | |
781 | + } | |
782 | + escaped[j] = name[i]; | |
783 | + } | |
784 | +} | |
785 | + | |
786 | + | |
787 | + | |
788 | +#endif | |
789 | + | |
0b72d562 | 790 | --- postfix-2.0.16.orig/src/util/dict_pgsql.h 1970-01-01 01:00:00.000000000 +0100 |
791 | +++ postfix-2.0.16/src/util/dict_pgsql.h 2003-11-08 20:11:44.000000000 +0100 | |
f512ae96 JB |
792 | @@ -0,0 +1,41 @@ |
793 | +#ifndef _DICT_PGSQL_H_INCLUDED_ | |
794 | +#define _DICT_PGSQL_H_INCLUDED_ | |
795 | + | |
796 | +/*++ | |
797 | +/* NAME | |
798 | +/* dict_pgsql 3h | |
799 | +/* SUMMARY | |
800 | +/* dictionary manager interface to pgsql databases | |
801 | +/* SYNOPSIS | |
802 | +/* #include <dict_pgsql.h> | |
803 | +/* DESCRIPTION | |
804 | +/* .nf | |
805 | + | |
806 | + /* | |
807 | + * Utility library. | |
808 | + */ | |
809 | +#include <dict.h> | |
810 | + | |
811 | + /* | |
812 | + * External interface. | |
813 | + */ | |
814 | +#define DICT_TYPE_PGSQL "pgsql" | |
815 | + | |
816 | +extern DICT *dict_pgsql_open (const char *, int, int); | |
817 | + | |
818 | +/* LICENSE | |
819 | +/* .ad | |
820 | +/* .fi | |
821 | +/* The Secure Mailer license must be distributed with this software. | |
822 | +/* AUTHOR(S) | |
823 | +/* Scott Cotton | |
824 | +/* IC Group, Inc. | |
825 | +/* scott@icgroup.com | |
826 | +/* | |
827 | +/* Joshua Marcus | |
828 | +/* IC Group, Inc. | |
829 | +/* josh@icgroup.com | |
830 | +/*--*/ | |
831 | + | |
832 | +#endif | |
833 | + | |
0b72d562 | 834 | --- postfix-2.0.16.orig/src/util/Makefile.in 2003-11-08 20:10:08.000000000 +0100 |
835 | +++ postfix-2.0.16/src/util/Makefile.in 2003-11-08 20:12:29.000000000 +0100 | |
836 | @@ -4,7 +4,7 @@ | |
837 | chroot_uid.c clean_env.c close_on_exec.c concatenate.c ctable.c \ | |
838 | dict.c dict_alloc.c dict_db.c dict_dbm.c dict_debug.c dict_env.c \ | |
839 | dict_ht.c dict_mysql.c dict_ni.c dict_nis.c \ | |
840 | - dict_nisplus.c dict_open.c dict_pcre.c dict_regexp.c dict_static.c \ | |
841 | + dict_nisplus.c dict_open.c dict_pcre.c dict_pgsql.c dict_regexp.c dict_static.c \ | |
842 | dict_tcp.c dict_unix.c dir_forest.c doze.c duplex_pipe.c \ | |
843 | environ.c events.c exec_command.c fifo_listen.c fifo_trigger.c \ | |
844 | file_limit.c find_inet.c fsspace.c fullname.c get_domainname.c \ | |
845 | @@ -60,7 +60,7 @@ | |
846 | HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \ | |
847 | connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \ | |
848 | dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \ | |
849 | - dict_nisplus.h dict_pcre.h dict_regexp.h dict_static.h dict_tcp.h \ | |
850 | + dict_nisplus.h dict_pcre.h dict_pgsql.h dict_regexp.h dict_static.h dict_tcp.h \ | |
851 | dict_unix.h dir_forest.h events.h exec_command.h find_inet.h \ | |
852 | fsspace.h fullname.h get_domainname.h get_hostname.h hex_quote.h \ | |
853 | htable.h inet_addr_host.h inet_addr_list.h inet_addr_local.h \ | |
854 | @@ -86,6 +86,7 @@ | |
855 | INCL = | |
856 | PCRESO = dict_pcre.so | |
857 | MYSQLSO = dict_mysql.so | |
858 | +PGSQLSO = dict_pgsql.so | |
859 | LIB = libutil.a | |
860 | TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ | |
861 | fifo_rdonly_bug fifo_rdwr_bug fifo_trigger fsspace fullname \ | |
862 | @@ -98,7 +99,7 @@ | |
863 | ||
864 | LIB_DIR = ../../lib | |
865 | INC_DIR = ../../include | |
866 | -LIBS = $(LIB_DIR)/$(LIB) $(LIB_DIR)/$(PCRESO) $(LIB_DIR)/$(MYSQLSO) | |
867 | +LIBS = $(LIB_DIR)/$(LIB) $(LIB_DIR)/$(PCRESO) $(LIB_DIR)/$(MYSQLSO) $(LIB_DIR)/$(PGSQLSO) | |
868 | ||
869 | .c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c | |
870 | ||
871 | @@ -115,6 +116,9 @@ | |
872 | $(MYSQLSO): dict_mysql.o | |
873 | gcc -shared -Wl,-soname,dict_mysql.so -o $@ $? -lmysqlclient -L. -lutil | |
874 | ||
875 | +$(PGSQLSO): dict_pgsql.o | |
876 | + gcc -shared -Wl,-soname,dict_pgsql.so -o $@ $? -lpq -lcrypt -L. -lutil | |
877 | + | |
878 | $(LIB): $(OBJS) | |
879 | gcc -shared -Wl,-soname,libpostfix-util.so.1 -o $(LIB) $(OBJS) -ldl | |
880 | ||
881 | @@ -127,6 +131,9 @@ | |
882 | $(LIB_DIR)/$(MYSQLSO): $(MYSQLSO) | |
883 | cp $(MYSQLSO) $(LIB_DIR) | |
884 | ||
885 | +$(LIB_DIR)/$(PGSQLSO): $(PGSQLSO) | |
886 | + cp $(PGSQLSO) $(LIB_DIR) | |
887 | + | |
888 | update: $(LIBS) $(HDRS) | |
889 | -for i in $(HDRS); \ | |
890 | do \ | |
891 | @@ -149,7 +156,7 @@ | |
892 | lint $(SRCS) | |
893 | ||
894 | clean: | |
895 | - rm -f *.o $(LIB) $(PCRESO) $(MYSQLSO) *core $(TESTPROG) \ | |
896 | + rm -f *.o $(LIB) $(PCRESO) $(MYSQLSO) $(PGSQLSO) *core $(TESTPROG) \ | |
897 | junk $(MAKES) *.tmp | |
898 | rm -rf printfck | |
899 | ||
900 | @@ -591,6 +598,8 @@ | |
901 | dict_ldap.o: sys_defs.h | |
902 | dict_mysql.o: dict_mysql.c | |
903 | dict_mysql.o: sys_defs.h | |
904 | +dict_pgsql.o: dict_pgsql.c | |
905 | +dict_pgsql.o: sys_defs.h | |
906 | dict_ni.o: dict_ni.c | |
907 | dict_ni.o: sys_defs.h | |
908 | dict_nis.o: dict_nis.c | |
909 | @@ -632,6 +641,7 @@ | |
910 | dict_open.o: dict_ni.h | |
911 | dict_open.o: dict_ldap.h | |
912 | dict_open.o: dict_mysql.h | |
913 | +dict_open.o: dict_pgsql.h | |
914 | dict_open.o: dict_pcre.h | |
915 | dict_open.o: dict_regexp.h | |
916 | dict_open.o: dict_static.h |