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