Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / db_common.c
blob8954b183b35c8b3d1bfb7c8014c9c61d03b109e4
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* db_common 3
6 /* SUMMARY
7 /* utilities common to network based dictionaries
8 /* SYNOPSIS
9 /* #include "db_common.h"
11 /* int db_common_parse(dict, ctx, format, query)
12 /* DICT *dict;
13 /* void **ctx;
14 /* const char *format;
15 /* int query;
17 /* void db_common_free_context(ctx)
18 /* void *ctx;
20 /* int db_common_expand(ctx, format, value, key, buf, quote_func);
21 /* void *ctx;
22 /* const char *format;
23 /* const char *value;
24 /* const char *key;
25 /* VSTRING *buf;
26 /* void (*quote_func)(DICT *, const char *, VSTRING *);
28 /* int db_common_check_domain(domain_list, addr);
29 /* STRING_LIST *domain_list;
30 /* const char *addr;
32 /* void db_common_sql_build_query(query,parser);
33 /* VSTRING *query;
34 /* CFG_PARSER *parser;
36 /* DESCRIPTION
37 /* This module implements utilities common to network based dictionaries.
39 /* \fIdb_common_parse\fR parses query and result substitution templates.
40 /* It must be called for each template before any calls to
41 /* \fIdb_common_expand\fR. The \fIctx\fB argument must be initialized to
42 /* a reference to a (void *)0 before the first template is parsed, this
43 /* causes memory for the context to be allocated and the new pointer is
44 /* stored in *ctx. When the dictionary is closed, this memory must be
45 /* freed with a final call to \fBdb_common_free_context\fR.
47 /* Calls for additional templates associated with the same map must use the
48 /* same ctx argument. The context accumulates run-time lookup key and result
49 /* validation information (inapplicable keys or results are skipped) and is
50 /* needed later in each call of \fIdb_common_expand\fR. A non-zero return
51 /* value indicates that data-depedent '%' expansions were found in the input
52 /* template.
54 /* \fIdb_common_expand\fR expands the specifiers in \fIformat\fR.
55 /* When the input data lacks all fields needed for the expansion, zero
56 /* is returned and the query or result should be skipped. Otherwise
57 /* the expansion is appended to the result buffer (after a comma if the
58 /* the result buffer is not empty).
60 /* If not NULL, the \fBquote_func\fR callback performs database-specific
61 /* quoting of each variable before expansion.
62 /* \fBvalue\fR is the lookup key for query expansion and result for result
63 /* expansion. \fBkey\fR is NULL for query expansion and the lookup key for
64 /* result expansion.
65 /* .PP
66 /* The following '%' expansions are performed on \fBvalue\fR:
67 /* .IP %%
68 /* A literal percent character.
69 /* .IP %s
70 /* The entire lookup key \fIaddr\fR.
71 /* .IP %u
72 /* If \fBaddr\fR is a fully qualified address, the local part of the
73 /* address. Otherwise \fIaddr\fR.
74 /* .IP %d
75 /* If \fIaddr\fR is a fully qualified address, the domain part of the
76 /* address. Otherwise the query against the database is suppressed and
77 /* the lookup returns no results.
79 /* The following '%' expansions are performed on the lookup \fBkey\fR:
80 /* .IP %S
81 /* The entire lookup key \fIkey\fR.
82 /* .IP %U
83 /* If \fBkey\fR is a fully qualified address, the local part of the
84 /* address. Otherwise \fIkey\fR.
85 /* .IP %D
86 /* If \fIkey\fR is a fully qualified address, the domain part of the
87 /* address. Otherwise the query against the database is suppressed and
88 /* the lookup returns no results.
90 /* .PP
91 /* \fIdb_common_check_domain\fR checks domain list so that query optimization
92 /* can be performed
94 /* .PP
95 /* \fIdb_common_sql_build_query\fR builds the "default"(backwards compatible)
96 /* query from the 'table', 'select_field', 'where_field' and
97 /* 'additional_conditions' parameters, checking for errors.
99 /* DIAGNOSTICS
100 /* Fatal errors: invalid substitution format, invalid string_list pattern,
101 /* insufficient parameters.
102 /* SEE ALSO
103 /* dict(3) dictionary manager
104 /* string_list(3) string list pattern matching
105 /* match_ops(3) simple string or host pattern matching
106 /* LICENSE
107 /* .ad
108 /* .fi
109 /* The Secure Mailer license must be distributed with this software.
110 /* AUTHOR(S)
111 /* Wietse Venema
112 /* IBM T.J. Watson Research
113 /* P.O. Box 704
114 /* Yorktown Heights, NY 10598, USA
116 /* Liviu Daia
117 /* Institute of Mathematics of the Romanian Academy
118 /* P.O. BOX 1-764
119 /* RO-014700 Bucharest, ROMANIA
121 /* Jose Luis Tallon
122 /* G4 J.E. - F.I. - U.P.M.
123 /* Campus de Montegancedo, S/N
124 /* E-28660 Madrid, SPAIN
126 /* Victor Duchovni
127 /* Morgan Stanley
128 /*--*/
131 * System library.
133 #include "sys_defs.h"
134 #include <stddef.h>
135 #include <string.h>
138 * Global library.
140 #include "cfg_parser.h"
143 * Utility library.
145 #include <mymalloc.h>
146 #include <vstring.h>
147 #include <msg.h>
148 #include <dict.h>
151 * Application specific
153 #include "db_common.h"
155 #define DB_COMMON_KEY_DOMAIN (1 << 0)/* Need lookup key domain */
156 #define DB_COMMON_KEY_USER (1 << 1)/* Need lookup key localpart */
157 #define DB_COMMON_VALUE_DOMAIN (1 << 2)/* Need result domain */
158 #define DB_COMMON_VALUE_USER (1 << 3)/* Need result localpart */
159 #define DB_COMMON_KEY_PARTIAL (1 << 4)/* Key uses input substrings */
161 typedef struct {
162 DICT *dict;
163 STRING_LIST *domain;
164 int flags;
165 int nparts;
166 } DB_COMMON_CTX;
168 /* db_common_parse - validate query or result template */
170 int db_common_parse(DICT *dict, void **ctxPtr, const char *format, int query)
172 DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) * ctxPtr;
173 const char *cp;
174 int dynamic = 0;
176 if (ctx == 0) {
177 ctx = (DB_COMMON_CTX *) (*ctxPtr = mymalloc(sizeof *ctx));
178 ctx->dict = dict;
179 ctx->domain = 0;
180 ctx->flags = 0;
181 ctx->nparts = 0;
183 for (cp = format; *cp; ++cp)
184 if (*cp == '%')
185 switch (*++cp) {
186 case '%':
187 break;
188 case 'u':
189 ctx->flags |=
190 query ? DB_COMMON_KEY_USER | DB_COMMON_KEY_PARTIAL
191 : DB_COMMON_VALUE_USER;
192 dynamic = 1;
193 break;
194 case 'd':
195 ctx->flags |=
196 query ? DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_PARTIAL
197 : DB_COMMON_VALUE_DOMAIN;
198 dynamic = 1;
199 break;
200 case 's':
201 case 'S':
202 dynamic = 1;
203 break;
204 case 'U':
205 ctx->flags |= DB_COMMON_KEY_PARTIAL | DB_COMMON_KEY_USER;
206 dynamic = 1;
207 break;
208 case '1':
209 case '2':
210 case '3':
211 case '4':
212 case '5':
213 case '6':
214 case '7':
215 case '8':
216 case '9':
219 * Find highest %[1-9] index in query template. Input keys
220 * will be constrained to those with at least this many
221 * domain components. This makes the db_common_expand() code
222 * safe from invalid inputs.
224 if (ctx->nparts < *cp - '0')
225 ctx->nparts = *cp - '0';
226 /* FALLTHROUGH */
227 case 'D':
228 ctx->flags |= DB_COMMON_KEY_PARTIAL | DB_COMMON_KEY_DOMAIN;
229 dynamic = 1;
230 break;
231 default:
232 msg_fatal("db_common_parse: %s: Invalid %s template: %s",
233 dict->name, query ? "query" : "result", format);
235 return dynamic;
238 /* db_common_parse_domain - parse domain matchlist*/
240 void db_common_parse_domain(CFG_PARSER *parser, void *ctxPtr)
242 DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxPtr;
243 char *domainlist;
244 const char *myname = "db_common_parse_domain";
246 domainlist = cfg_get_str(parser, "domain", "", 0, 0);
247 if (*domainlist) {
248 ctx->domain = string_list_init(MATCH_FLAG_NONE, domainlist);
249 if (ctx->domain == 0)
252 * The "domain" optimization skips input keys that may in fact
253 * have unwanted matches in the database, so failure to create
254 * the match list is fatal.
256 msg_fatal("%s: %s: domain match list creation using '%s' failed",
257 myname, parser->name, domainlist);
259 myfree(domainlist);
262 /* db_common_dict_partial - Does query use partial lookup keys? */
264 int db_common_dict_partial(void *ctxPtr)
266 #if 0 /* Breaks recipient_delimiter */
267 DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxPtr;
269 return (ctx->domain || ctx->flags & DB_COMMON_KEY_PARTIAL);
270 #endif
271 return (0);
274 /* db_common_free_ctx - free parse context */
276 void db_common_free_ctx(void *ctxPtr)
278 DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxPtr;
280 if (ctx->domain)
281 string_list_free(ctx->domain);
282 myfree((char *) ctxPtr);
285 /* db_common_expand - expand query and result templates */
287 int db_common_expand(void *ctxArg, const char *format, const char *value,
288 const char *key, VSTRING *result,
289 db_quote_callback_t quote_func)
291 const char *myname = "db_common_expand";
292 DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxArg;
293 const char *vdomain = 0;
294 const char *kdomain = 0;
295 const char *domain = 0;
296 int dflag = key ? DB_COMMON_VALUE_DOMAIN : DB_COMMON_KEY_DOMAIN;
297 char *vuser = 0;
298 char *kuser = 0;
299 ARGV *parts = 0;
300 int i;
301 const char *cp;
303 /* Skip NULL values, silently. */
304 if (value == 0)
305 return (0);
307 /* Don't silenty skip empty query string or empty lookup results. */
308 if (*value == 0) {
309 if (key)
310 msg_warn("table \"%s:%s\": empty lookup result for: \"%s\""
311 " -- ignored", ctx->dict->type, ctx->dict->name, key);
312 else
313 msg_warn("table \"%s:%s\": empty query string"
314 " -- ignored", ctx->dict->type, ctx->dict->name);
315 return (0);
317 if (key) {
318 /* This is a result template and the input value is the result */
319 if (ctx->flags & (DB_COMMON_VALUE_DOMAIN | DB_COMMON_VALUE_USER))
320 if ((vdomain = strrchr(value, '@')) != 0)
321 ++vdomain;
323 if (((!vdomain || !*vdomain) && (ctx->flags & DB_COMMON_VALUE_DOMAIN) != 0)
324 || (vdomain == value + 1 && (ctx->flags & DB_COMMON_VALUE_USER) != 0))
325 return (0);
327 /* The result format may use the local or domain part of the key */
328 if (ctx->flags & (DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_USER))
329 if ((kdomain = strrchr(key, '@')) != 0)
330 ++kdomain;
333 * The key should already be checked before the query. No harm if the
334 * query did not get optimized out, so we just issue a warning.
336 if (((!kdomain || !*kdomain) && (ctx->flags & DB_COMMON_KEY_DOMAIN) != 0)
337 || (kdomain == key + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0)) {
338 msg_warn("%s: %s: lookup key '%s' skipped after query", myname,
339 ctx->dict->name, value);
340 return (0);
342 } else {
343 /* This is a query template and the input value is the lookup key */
344 if (ctx->flags & (DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_USER))
345 if ((vdomain = strrchr(value, '@')) != 0)
346 ++vdomain;
348 if (((!vdomain || !*vdomain) && (ctx->flags & DB_COMMON_KEY_DOMAIN) != 0)
349 || (vdomain == value + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0))
350 return (0);
353 if (ctx->nparts > 0) {
354 parts = argv_split(key ? kdomain : vdomain, ".");
357 * Filter out input keys whose domains lack enough labels to fill-in
358 * the query template. See below and also db_common_parse() which
359 * initializes ctx->nparts.
361 if (parts->argc < ctx->nparts) {
362 argv_free(parts);
363 return (0);
367 * Skip domains with leading, consecutive or trailing '.' separators
368 * among the required labels.
370 for (i = 0; i < ctx->nparts; i++)
371 if (*parts->argv[parts->argc - i - 1] == 0) {
372 argv_free(parts);
373 return (0);
376 if (VSTRING_LEN(result) > 0)
377 VSTRING_ADDCH(result, ',');
379 #define QUOTE_VAL(d, q, v, buf) do { \
380 if (q) \
381 q(d, v, buf); \
382 else \
383 vstring_strcat(buf, v); \
384 } while (0)
387 * Replace all instances of %s with the address to look up. Replace %u
388 * with the user portion, and %d with the domain portion. "%%" expands to
389 * "%". lowercase -> addr, uppercase -> key
391 for (cp = format; *cp; cp++) {
392 if (*cp == '%') {
393 switch (*++cp) {
395 case '%':
396 VSTRING_ADDCH(result, '%');
397 break;
399 case 's':
400 QUOTE_VAL(ctx->dict, quote_func, value, result);
401 break;
403 case 'u':
404 if (vdomain) {
405 if (vuser == 0)
406 vuser = mystrndup(value, vdomain - value - 1);
407 QUOTE_VAL(ctx->dict, quote_func, vuser, result);
408 } else
409 QUOTE_VAL(ctx->dict, quote_func, value, result);
410 break;
412 case 'd':
413 if (!(ctx->flags & dflag))
414 msg_panic("%s: %s: %s: bad query/result template context",
415 myname, ctx->dict->name, format);
416 if (!vdomain)
417 msg_panic("%s: %s: %s: expanding domain-less key or value",
418 myname, ctx->dict->name, format);
419 QUOTE_VAL(ctx->dict, quote_func, vdomain, result);
420 break;
422 case 'S':
423 if (key)
424 QUOTE_VAL(ctx->dict, quote_func, key, result);
425 else
426 QUOTE_VAL(ctx->dict, quote_func, value, result);
427 break;
429 case 'U':
430 if (key) {
431 if (kdomain) {
432 if (kuser == 0)
433 kuser = mystrndup(key, kdomain - key - 1);
434 QUOTE_VAL(ctx->dict, quote_func, kuser, result);
435 } else
436 QUOTE_VAL(ctx->dict, quote_func, key, result);
437 } else {
438 if (vdomain) {
439 if (vuser == 0)
440 vuser = mystrndup(value, vdomain - value - 1);
441 QUOTE_VAL(ctx->dict, quote_func, vuser, result);
442 } else
443 QUOTE_VAL(ctx->dict, quote_func, value, result);
445 break;
447 case 'D':
448 if (!(ctx->flags & DB_COMMON_KEY_DOMAIN))
449 msg_panic("%s: %s: %s: bad query/result template context",
450 myname, ctx->dict->name, format);
451 if ((domain = key ? kdomain : vdomain) == 0)
452 msg_panic("%s: %s: %s: expanding domain-less key or value",
453 myname, ctx->dict->name, format);
454 QUOTE_VAL(ctx->dict, quote_func, domain, result);
455 break;
457 case '1':
458 case '2':
459 case '3':
460 case '4':
461 case '5':
462 case '6':
463 case '7':
464 case '8':
465 case '9':
468 * Interpolate %[1-9] components into the query string. By
469 * this point db_common_parse() has identified the highest
470 * component index, and (see above) keys with fewer
471 * components have been filtered out. The "parts" ARGV is
472 * guaranteed to be initialized and hold enough elements to
473 * satisfy the query template.
475 if (!(ctx->flags & DB_COMMON_KEY_DOMAIN)
476 || ctx->nparts < *cp - '0')
477 msg_panic("%s: %s: %s: bad query/result template context",
478 myname, ctx->dict->name, format);
479 if (!parts || parts->argc < ctx->nparts)
480 msg_panic("%s: %s: %s: key has too few domain labels",
481 myname, ctx->dict->name, format);
482 QUOTE_VAL(ctx->dict, quote_func,
483 parts->argv[parts->argc - (*cp - '0')], result);
484 break;
486 default:
487 msg_fatal("%s: %s: invalid %s template '%s'", myname,
488 ctx->dict->name, key ? "result" : "query",
489 format);
491 } else
492 VSTRING_ADDCH(result, *cp);
494 VSTRING_TERMINATE(result);
496 if (vuser)
497 myfree(vuser);
498 if (kuser)
499 myfree(kuser);
500 if (parts)
501 argv_free(parts);
503 return (1);
507 /* db_common_check_domain - check domain list */
509 int db_common_check_domain(void *ctxPtr, const char *addr)
511 DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxPtr;
512 char *domain;
514 if (ctx->domain) {
515 if ((domain = strrchr(addr, '@')) != NULL)
516 ++domain;
517 if (domain == NULL || domain == addr + 1)
518 return (0);
519 if (match_list_match(ctx->domain, domain) == 0)
520 return (0);
522 return (1);
525 /* db_common_sql_build_query -- build query for SQL maptypes */
527 void db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser)
529 const char *myname = "db_common_sql_build_query";
530 char *table;
531 char *select_field;
532 char *where_field;
533 char *additional_conditions;
536 * Build "old style" query: "select %s from %s where %s"
538 if ((table = cfg_get_str(parser, "table", NULL, 1, 0)) == 0)
539 msg_fatal("%s: 'table' parameter not defined", myname);
541 if ((select_field = cfg_get_str(parser, "select_field", NULL, 1, 0)) == 0)
542 msg_fatal("%s: 'select_field' parameter not defined", myname);
544 if ((where_field = cfg_get_str(parser, "where_field", NULL, 1, 0)) == 0)
545 msg_fatal("%s: 'where_field' parameter not defined", myname);
547 additional_conditions = cfg_get_str(parser, "additional_conditions",
548 "", 0, 0);
550 vstring_sprintf(query, "SELECT %s FROM %s WHERE %s='%%s' %s",
551 select_field, table, where_field,
552 additional_conditions);
554 myfree(table);
555 myfree(select_field);
556 myfree(where_field);
557 myfree(additional_conditions);