xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict_union.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: dict_union.c,v 1.2 2017/02/14 01:16:49 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dict_union 3
6 /* SUMMARY
7 /*	dictionary manager interface for union of tables
8 /* SYNOPSIS
9 /*	#include <dict_union.h>
10 /*
11 /*	DICT	*dict_union_open(name, open_flags, dict_flags)
12 /*	const char *name;
13 /*	int	open_flags;
14 /*	int	dict_flags;
15 /* DESCRIPTION
16 /*	dict_union_open() opens a sequence of one or more tables.
17 /*	Example: "\fBunionmap:{\fItype_1:name_1, ..., type_n:name_n\fR}".
18 /*
19 /*	Each "unionmap:" query is given to each table in the specified
20 /*	order. All found results are concatenated, separated by
21 /*	comma.  The unionmap table produces no result when all
22 /*	lookup tables return no result.
23 /*
24 /*	The first and last characters of a "unionmap:" table name
25 /*	must be '{' and '}'. Within these, individual maps are
26 /*	separated with comma or whitespace.
27 /*
28 /*	The open_flags and dict_flags arguments are passed on to
29 /*	the underlying dictionaries.
30 /* SEE ALSO
31 /*	dict(3) generic dictionary manager
32 /* LICENSE
33 /* .ad
34 /* .fi
35 /*	The Secure Mailer license must be distributed with this software.
36 /* AUTHOR(S)
37 /*	Wietse Venema
38 /*	IBM T.J. Watson Research
39 /*	P.O. Box 704
40 /*	Yorktown Heights, NY 10598, USA
41 /*--*/
42 
43 /* System library. */
44 
45 #include <sys_defs.h>
46 #include <string.h>
47 
48 /* Utility library. */
49 
50 #include <msg.h>
51 #include <mymalloc.h>
52 #include <htable.h>
53 #include <dict.h>
54 #include <dict_union.h>
55 #include <stringops.h>
56 #include <vstring.h>
57 
58 /* Application-specific. */
59 
60 typedef struct {
61     DICT    dict;			/* generic members */
62     ARGV   *map_union;			/* pipelined tables */
63     VSTRING *re_buf;			/* reply buffer */
64 } DICT_UNION;
65 
66 #define STR(x) vstring_str(x)
67 
68 /* dict_union_lookup - search a bunch of tables and combine the results */
69 
70 static const char *dict_union_lookup(DICT *dict, const char *query)
71 {
72     static const char myname[] = "dict_union_lookup";
73     DICT_UNION *dict_union = (DICT_UNION *) dict;
74     DICT   *map;
75     char  **cpp;
76     char   *dict_type_name;
77     const char *result = 0;
78 
79     /*
80      * After Roel van Meer, postfix-users mailing list, Sept 2014.
81      */
82     VSTRING_RESET(dict_union->re_buf);
83     for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) {
84 	if ((map = dict_handle(dict_type_name)) == 0)
85 	    msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name);
86 	if ((result = dict_get(map, query)) != 0) {
87 	    if (VSTRING_LEN(dict_union->re_buf) > 0)
88 		VSTRING_ADDCH(dict_union->re_buf, ',');
89 	    vstring_strcat(dict_union->re_buf, result);
90 	} else if (map->error != 0) {
91 	    DICT_ERR_VAL_RETURN(dict, map->error, 0);
92 	}
93     }
94     DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE,
95 			VSTRING_LEN(dict_union->re_buf) > 0 ?
96 			STR(dict_union->re_buf) : 0);
97 }
98 
99 /* dict_union_close - disassociate from a bunch of tables */
100 
101 static void dict_union_close(DICT *dict)
102 {
103     DICT_UNION *dict_union = (DICT_UNION *) dict;
104     char  **cpp;
105     char   *dict_type_name;
106 
107     for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++)
108 	dict_unregister(dict_type_name);
109     argv_free(dict_union->map_union);
110     vstring_free(dict_union->re_buf);
111     dict_free(dict);
112 }
113 
114 /* dict_union_open - open a bunch of tables */
115 
116 DICT   *dict_union_open(const char *name, int open_flags, int dict_flags)
117 {
118     static const char myname[] = "dict_union_open";
119     DICT_UNION *dict_union;
120     char   *saved_name = 0;
121     char   *dict_type_name;
122     ARGV   *argv = 0;
123     char  **cpp;
124     DICT   *dict;
125     int     match_flags = 0;
126     struct DICT_OWNER aggr_owner;
127     size_t  len;
128 
129     /*
130      * Clarity first. Let the optimizer worry about redundant code.
131      */
132 #define DICT_UNION_RETURN(x) do { \
133 	      if (saved_name != 0) \
134 	          myfree(saved_name); \
135 	      if (argv != 0) \
136 	          argv_free(argv); \
137 	      return (x); \
138 	  } while (0)
139 
140     /*
141      * Sanity checks.
142      */
143     if (open_flags != O_RDONLY)
144 	DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name,
145 					 open_flags, dict_flags,
146 				  "%s:%s map requires O_RDONLY access mode",
147 					 DICT_TYPE_UNION, name));
148 
149     /*
150      * Split the table name into its constituent parts.
151      */
152     if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0
153 	|| *(saved_name = mystrndup(name + 1, len - 2)) == 0
154 	|| ((argv = argv_splitq(saved_name, CHARS_COMMA_SP, CHARS_BRACE)),
155 	    (argv->argc == 0)))
156 	DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name,
157 					 open_flags, dict_flags,
158 					 "bad syntax: \"%s:%s\"; "
159 					 "need \"%s:{type:name...}\"",
160 					 DICT_TYPE_UNION, name,
161 					 DICT_TYPE_UNION));
162 
163     /*
164      * The least-trusted table in the set determines the over-all trust
165      * level. The first table determines the pattern-matching flags.
166      */
167     DICT_OWNER_AGGREGATE_INIT(aggr_owner);
168     for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
169 	if (msg_verbose)
170 	    msg_info("%s: %s", myname, dict_type_name);
171 	if (strchr(dict_type_name, ':') == 0)
172 	    DICT_UNION_RETURN(dict_surrogate(DICT_TYPE_UNION, name,
173 					     open_flags, dict_flags,
174 					     "bad syntax: \"%s:%s\"; "
175 					     "need \"%s:{type:name...}\"",
176 					     DICT_TYPE_UNION, name,
177 					     DICT_TYPE_UNION));
178 	if ((dict = dict_handle(dict_type_name)) == 0)
179 	    dict = dict_open(dict_type_name, open_flags, dict_flags);
180 	dict_register(dict_type_name, dict);
181 	DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
182 	if (cpp == argv->argv)
183 	    match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
184     }
185 
186     /*
187      * Bundle up the result.
188      */
189     dict_union =
190 	(DICT_UNION *) dict_alloc(DICT_TYPE_UNION, name, sizeof(*dict_union));
191     dict_union->dict.lookup = dict_union_lookup;
192     dict_union->dict.close = dict_union_close;
193     dict_union->dict.flags = dict_flags | match_flags;
194     dict_union->dict.owner = aggr_owner;
195     dict_union->re_buf = vstring_alloc(100);
196     dict_union->map_union = argv;
197     argv = 0;
198     DICT_UNION_RETURN(DICT_DEBUG (&dict_union->dict));
199 }
200