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