1/*++
2/* NAME
3/*	match_service 3
4/* SUMMARY
5/*	simple master.cf service name.type pattern matcher
6/* SYNOPSIS
7/*	#include <match_service.h>
8/*
9/*	ARGV	*match_service_init(pattern_list)
10/*	const char *pattern_list;
11/*
12/*	ARGV	*match_service_init_argv(pattern_list)
13/*	char	**pattern_list;
14/*
15/*	int	match_service_match(list, name_type)
16/*	ARGV	*list;
17/*	const char *name_type;
18/*
19/*	void match_service_free(list)
20/*	ARGV	*list;
21/* DESCRIPTION
22/*	This module implements pattern matching for Postfix master.cf
23/*	services.  This is more precise than using domain_list(3),
24/*	because match_service(3) won't treat a dotted service name
25/*	as a domain hierarchy. Moreover, this module has the advantage
26/*	that it does not drag in all the LDAP, SQL and other map
27/*	lookup client code into programs that don't need it.
28/*
29/*	Each pattern is of the form "name/type" or "type", where
30/*	"name" and "type" are the first two fields of a master.cf
31/*	entry. Patterns are separated by whitespace and/or commas.
32/*	Matches are case insensitive. Patterns are matched in the
33/*	specified order, and the matching process stops at the first
34/*	match.  In order to reverse the result of a pattern match,
35/*	precede a pattern with an exclamation point (!).
36/*
37/*	For backwards compatibility, the form name.type is still
38/*	supported.
39/*
40/*	match_service_init() parses the pattern list. The result
41/*	must be passed to match_service_match() or match_service_free().
42/*
43/*	match_service_init_argv() provides an alternate interface
44/*	for pre-parsed strings.
45/*
46/*	match_service_match() matches one service name.type string
47/*	against the specified pattern list.
48/*
49/*	match_service_free() releases storage allocated by
50/*	match_service_init().
51/* DIAGNOSTICS
52/*	Fatal error: out of memory, malformed pattern.
53/*	Panic: malformed search string.
54/* SEE ALSO
55/*	domain_list(3) match domain names.
56/* LICENSE
57/* .ad
58/* .fi
59/*	The Secure Mailer license must be distributed with this software.
60/* AUTHOR(S)
61/*	Wietse Venema
62/*	IBM T.J. Watson Research
63/*	P.O. Box 704
64/*	Yorktown Heights, NY 10598, USA
65/*--*/
66
67/* System library. */
68
69#include <sys_defs.h>
70#include <string.h>
71
72#ifdef STRCASECMP_IN_STRINGS_H
73#include <strings.h>
74#endif
75
76/* Utility library. */
77
78#include <msg.h>
79#include <argv.h>
80#include <mymalloc.h>
81#include <stringops.h>
82#include <match_service.h>
83
84/* match_service_compat - backwards compatibility */
85
86static void match_service_compat(ARGV *argv)
87{
88    char  **cpp;
89    char   *cp;
90
91    for (cpp = argv->argv; *cpp; cpp++) {
92	if (strrchr(*cpp, '/') == 0 && (cp = strrchr(*cpp, '.')) != 0)
93	    *cp = '/';
94    }
95}
96
97/* match_service_init - initialize pattern list */
98
99ARGV   *match_service_init(const char *patterns)
100{
101    const char *delim = " ,\t\r\n";
102    ARGV   *list = argv_alloc(1);
103    char   *saved_patterns = mystrdup(patterns);
104    char   *bp = saved_patterns;
105    const char *item;
106
107    while ((item = mystrtok(&bp, delim)) != 0)
108	argv_add(list, item, (char *) 0);
109    argv_terminate(list);
110    myfree(saved_patterns);
111    match_service_compat(list);
112    return (list);
113}
114
115/* match_service_init_argv - impedance adapter */
116
117ARGV   *match_service_init_argv(char **patterns)
118{
119    ARGV   *list = argv_alloc(1);
120    char  **cpp;
121
122    for (cpp = patterns; *cpp; cpp++)
123	argv_add(list, *cpp, (char *) 0);
124    argv_terminate(list);
125    match_service_compat(list);
126    return (list);
127}
128
129/* match_service_match - match service name.type against pattern list */
130
131int     match_service_match(ARGV *list, const char *name_type)
132{
133    const char *myname = "match_service_match";
134    const char *type;
135    char  **cpp;
136    char   *pattern;
137    int     match;
138
139    /*
140     * Quick check for empty list.
141     */
142    if (list->argv[0] == 0)
143	return (0);
144
145    /*
146     * Sanity check.
147     */
148    if ((type = strrchr(name_type, '/')) == 0 || *++type == 0)
149	msg_panic("%s: malformed service: \"%s\"; need \"name/type\" format",
150		  myname, name_type);
151
152    /*
153     * Iterate over all patterns in the list, stop at the first match.
154     */
155    for (cpp = list->argv; (pattern = *cpp) != 0; cpp++) {
156	if (msg_verbose)
157	    msg_info("%s: %s ~? %s", myname, name_type, pattern);
158	for (match = 1; *pattern == '!'; pattern++)
159	    match = !match;
160	if (strcasecmp(strchr(pattern, '/') ? name_type : type, pattern) == 0) {
161	    if (msg_verbose)
162		msg_info("%s: %s: found match", myname, name_type);
163	    return (match);
164	}
165    }
166    if (msg_verbose)
167	msg_info("%s: %s: no match", myname, name_type);
168    return (0);
169}
170
171/* match_service_free - release storage */
172
173void    match_service_free(ARGV *list)
174{
175    argv_free(list);
176}
177