xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/match_list.c (revision c3ab26950fe8540fb553d1d1dcae454bc98e5a25)
1 /*	$NetBSD: match_list.c,v 1.1.1.3 2013/01/02 18:59:13 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	match_list 3
6 /* SUMMARY
7 /*	generic list-based pattern matching
8 /* SYNOPSIS
9 /*	#include <match_list.h>
10 /*
11 /*	MATCH_LIST *match_list_init(flags, pattern_list, count, func,...)
12 /*	int	flags;
13 /*	const char *pattern_list;
14 /*	int	count;
15 /*	int	(*func)(int flags, const char *string, const char *pattern);
16 /*
17 /*	int	match_list_match(list, string,...)
18 /*	MATCH_LIST *list;
19 /*	const char *string;
20 /*
21 /*	void match_list_free(list)
22 /*	MATCH_LIST *list;
23 /* DESCRIPTION
24 /*	This module implements a framework for tests for list membership.
25 /*	The actual tests are done by user-supplied functions.
26 /*
27 /*	Patterns are separated by whitespace and/or commas. A pattern
28 /*	is either a string, a file name (in which case the contents
29 /*	of the file are substituted for the file name) or a type:name
30 /*	lookup table specification. In order to reverse the result of
31 /*	a pattern match, precede a pattern with an exclamation point (!).
32 /*
33 /*	match_list_init() performs initializations. The flags argument
34 /*	specifies the bit-wise OR of zero or more of the following:
35 /* .RS
36 /* .IP MATCH_FLAG_PARENT
37 /*	The hostname pattern foo.com matches any name within the domain
38 /*	foo.com. If this flag is cleared, foo.com matches itself
39 /*	only, and .foo.com matches any name below the domain foo.com.
40 /* .IP MATCH_FLAG_RETURN
41 /*	Request that match_list_match() logs a warning and returns
42 /*	zero (with list->error set to a non-zero dictionary error
43 /*	code) instead of raising a fatal run-time error.
44 /* .RE
45 /*	Specify MATCH_FLAG_NONE to request none of the above.
46 /*	The pattern_list argument specifies a list of patterns.  The third
47 /*	argument specifies how many match functions follow.
48 /*
49 /*	match_list_match() matches strings against the specified pattern
50 /*	list, passing the first string to the first function given to
51 /*	match_list_init(), the second string to the second function, and
52 /*	so on.
53 /*
54 /*	match_list_free() releases storage allocated by match_list_init().
55 /* DIAGNOSTICS
56 /*	Fatal error: unable to open or read a match_list file; invalid
57 /*	match_list pattern.
58 /* SEE ALSO
59 /*	host_match(3) match hosts by name or by address
60 /* LICENSE
61 /* .ad
62 /* .fi
63 /*	The Secure Mailer license must be distributed with this software.
64 /* AUTHOR(S)
65 /*	Wietse Venema
66 /*	IBM T.J. Watson Research
67 /*	P.O. Box 704
68 /*	Yorktown Heights, NY 10598, USA
69 /*--*/
70 
71 /* System library. */
72 
73 #include <sys_defs.h>
74 #include <unistd.h>
75 #include <string.h>
76 #include <fcntl.h>
77 #include <stdlib.h>
78 #include <stdarg.h>
79 
80 /* Utility library. */
81 
82 #include <msg.h>
83 #include <mymalloc.h>
84 #include <vstring.h>
85 #include <vstream.h>
86 #include <vstring_vstream.h>
87 #include <stringops.h>
88 #include <argv.h>
89 #include <dict.h>
90 #include <match_list.h>
91 
92 /* Application-specific */
93 
94 #define MATCH_DICTIONARY(pattern) \
95     ((pattern)[0] != '[' && strchr((pattern), ':') != 0)
96 
97 /* match_list_parse - parse buffer, destroy buffer */
98 
99 static ARGV *match_list_parse(ARGV *list, char *string, int init_match)
100 {
101     const char *myname = "match_list_parse";
102     VSTRING *buf = vstring_alloc(10);
103     VSTREAM *fp;
104     const char *delim = " ,\t\r\n";
105     char   *bp = string;
106     char   *start;
107     char   *item;
108     char   *map_type_name_flags;
109     int     match;
110 
111 #define OPEN_FLAGS	O_RDONLY
112 #define DICT_FLAGS	(DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX)
113 #define STR(x)		vstring_str(x)
114 
115     /*
116      * /filename contents are expanded in-line. To support !/filename we
117      * prepend the negation operator to each item from the file.
118      */
119     while ((start = mystrtok(&bp, delim)) != 0) {
120 	if (*start == '#') {
121 	    msg_warn("%s: comment at end of line is not supported: %s %s",
122 		     myname, start, bp);
123 	    break;
124 	}
125 	for (match = init_match, item = start; *item == '!'; item++)
126 	    match = !match;
127 	if (*item == 0)
128 	    msg_fatal("%s: no pattern after '!'", myname);
129 	if (*item == '/') {			/* /file/name */
130 	    if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) {
131 		vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item);
132 		/* XXX Should increment existing map refcount. */
133 		if (dict_handle(STR(buf)) == 0)
134 		    dict_register(STR(buf),
135 				  dict_surrogate(DICT_TYPE_NOFILE, item,
136 						 OPEN_FLAGS, DICT_FLAGS,
137 						 "open file %s: %m", item));
138 		argv_add(list, STR(buf), (char *) 0);
139 	    } else {
140 		while (vstring_fgets(buf, fp))
141 		    if (vstring_str(buf)[0] != '#')
142 			list = match_list_parse(list, vstring_str(buf), match);
143 		if (vstream_fclose(fp))
144 		    msg_fatal("%s: read file %s: %m", myname, item);
145 	    }
146 	} else if (MATCH_DICTIONARY(item)) {	/* type:table */
147 	    vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!",
148 			    item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS));
149 	    map_type_name_flags = STR(buf) + (match == 0);
150 	    /* XXX Should increment existing map refcount. */
151 	    if (dict_handle(map_type_name_flags) == 0)
152 		dict_register(map_type_name_flags,
153 			      dict_open(item, OPEN_FLAGS, DICT_FLAGS));
154 	    argv_add(list, STR(buf), (char *) 0);
155 	} else {				/* other pattern */
156 	    argv_add(list, match ? item :
157 		     STR(vstring_sprintf(buf, "!%s", item)), (char *) 0);
158 	}
159     }
160     vstring_free(buf);
161     return (list);
162 }
163 
164 /* match_list_init - initialize pattern list */
165 
166 MATCH_LIST *match_list_init(int flags, const char *patterns, int match_count,...)
167 {
168     MATCH_LIST *list;
169     char   *saved_patterns;
170     va_list ap;
171     int     i;
172 
173     if (flags & ~MATCH_FLAG_ALL)
174 	msg_panic("match_list_init: bad flags 0x%x", flags);
175 
176     list = (MATCH_LIST *) mymalloc(sizeof(*list));
177     list->flags = flags;
178     list->match_count = match_count;
179     list->match_func =
180 	(MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN));
181     list->match_args =
182 	(const char **) mymalloc(match_count * sizeof(const char *));
183     va_start(ap, match_count);
184     for (i = 0; i < match_count; i++)
185 	list->match_func[i] = va_arg(ap, MATCH_LIST_FN);
186     va_end(ap);
187     list->error = 0;
188 
189 #define DO_MATCH	1
190 
191     saved_patterns = mystrdup(patterns);
192     list->patterns = match_list_parse(argv_alloc(1), saved_patterns, DO_MATCH);
193     argv_terminate(list->patterns);
194     myfree(saved_patterns);
195     return (list);
196 }
197 
198 /* match_list_match - match strings against pattern list */
199 
200 int     match_list_match(MATCH_LIST *list,...)
201 {
202     const char *myname = "match_list_match";
203     char  **cpp;
204     char   *pat;
205     int     match;
206     int     i;
207     va_list ap;
208 
209     /*
210      * Iterate over all patterns in the list, stop at the first match.
211      */
212     va_start(ap, list);
213     for (i = 0; i < list->match_count; i++)
214 	list->match_args[i] = va_arg(ap, const char *);
215     va_end(ap);
216 
217     list->error = 0;
218     for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) {
219 	for (match = 1; *pat == '!'; pat++)
220 	    match = !match;
221 	for (i = 0; i < list->match_count; i++)
222 	    if (list->match_func[i] (list, list->match_args[i], pat))
223 		return (match);
224 	    else if (list->error != 0)
225 		return (0);
226     }
227     if (msg_verbose)
228 	for (i = 0; i < list->match_count; i++)
229 	    msg_info("%s: %s: no match", myname, list->match_args[i]);
230     return (0);
231 }
232 
233 /* match_list_free - release storage */
234 
235 void    match_list_free(MATCH_LIST *list)
236 {
237     /* XXX Should decrement map refcounts. */
238     argv_free(list->patterns);
239     myfree((char *) list->match_func);
240     myfree((char *) list->match_args);
241     myfree((char *) list);
242 }
243