xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict_random.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
1 /*	$NetBSD: dict_random.c,v 1.4 2022/10/08 16:12:50 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dict_random 3
6 /* SUMMARY
7 /*	dictionary manager interface for randomized tables
8 /* SYNOPSIS
9 /*	#include <dict_random.h>
10 /*
11 /*	DICT	*dict_random_open(name, open_flags, dict_flags)
12 /*	const char *name;
13 /*	int	open_flags;
14 /*	int	dict_flags;
15 /* DESCRIPTION
16 /*	dict_random_open() opens an in-memory, read-only, table.
17 /*	Example: "\fBrandmap:{\fIresult_1, ... ,result_n}\fR".
18 /*
19 /*	Each table query returns a random choice from the specified
20 /*	results. Other table access methods are not supported.
21 /*
22 /*	The first and last characters of the "randmap:" table name
23 /*	must be '{' and '}'. Within these, individual maps are
24 /*	separated with comma or whitespace.
25 /* SEE ALSO
26 /*	dict(3) generic dictionary manager
27 /* LICENSE
28 /* .ad
29 /* .fi
30 /*	The Secure Mailer license must be distributed with this software.
31 /* AUTHOR(S)
32 /*	Wietse Venema
33 /*	IBM T.J. Watson Research
34 /*	P.O. Box 704
35 /*	Yorktown Heights, NY 10598, USA
36 /*
37 /*	Wietse Venema
38 /*	Google, Inc.
39 /*	111 8th Avenue
40 /*	New York, NY 10011, 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 <myrand.h>
53 #include <stringops.h>
54 #include <dict_random.h>
55 
56 /* Application-specific. */
57 
58 typedef struct {
59     DICT    dict;			/* generic members */
60     ARGV   *replies;			/* reply values */
61 } DICT_RANDOM;
62 
63 #define STR(x) vstring_str(x)
64 
65 /* dict_random_lookup - find randomized-table entry */
66 
dict_random_lookup(DICT * dict,const char * unused_query)67 static const char *dict_random_lookup(DICT *dict, const char *unused_query)
68 {
69     DICT_RANDOM *dict_random = (DICT_RANDOM *) dict;
70 
71     DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE,
72 	 dict_random->replies->argv[myrand() % dict_random->replies->argc]);
73 }
74 
75 /* dict_random_close - disassociate from randomized table */
76 
dict_random_close(DICT * dict)77 static void dict_random_close(DICT *dict)
78 {
79     DICT_RANDOM *dict_random = (DICT_RANDOM *) dict;
80 
81     if (dict_random->replies)
82 	argv_free(dict_random->replies);
83     dict_free(dict);
84 }
85 
dict_random_parse_name(DICT * dict,ARGV ** argv,const char * string,const char * delim,const char * parens)86 static char *dict_random_parse_name(DICT *dict, ARGV **argv,
87 				            const char *string,
88 				            const char *delim,
89 				            const char *parens)
90 {
91     ARGV   *argvp = argv_alloc(1);
92     char   *saved_string = mystrdup(string);
93     char   *bp = saved_string;
94     char   *arg;
95     VSTRING *b64 = 0;
96     char   *err = 0;
97 
98     while ((arg = mystrtokq(&bp, delim, parens)) != 0) {
99 	if (dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) {
100 	    if ((b64 = dict_file_to_b64(dict, arg)) != 0) {
101 		argv_add(argvp, vstring_str(b64), (char *) 0);
102 	    } else {
103 		err = dict_file_get_error(dict);
104 		break;
105 	    }
106 	} else {
107 	    argv_add(argvp, arg, (char *) 0);
108 	}
109     }
110     argv_terminate(argvp);
111     myfree(saved_string);
112     *argv = argvp;
113     return (err);
114 }
115 
116 /* dict_random_open - open a randomized table */
117 
dict_random_open(const char * name,int open_flags,int dict_flags)118 DICT   *dict_random_open(const char *name, int open_flags, int dict_flags)
119 {
120     DICT_RANDOM *dict_random;
121     char   *saved_name = 0;
122     size_t  len;
123     char   *err = 0;
124 
125     /*
126      * Clarity first. Let the optimizer worry about redundant code.
127      */
128 #define DICT_RANDOM_RETURN(x) do { \
129 	DICT *__d = (x); \
130 	if (saved_name != 0) \
131 	    myfree(saved_name); \
132 	if (err != 0) \
133 	    myfree(err); \
134 	return (__d); \
135     } while (0)
136 
137     /*
138      * Sanity checks.
139      */
140     if (open_flags != O_RDONLY)
141 	DICT_RANDOM_RETURN(dict_surrogate(DICT_TYPE_RANDOM, name,
142 					  open_flags, dict_flags,
143 				  "%s:%s map requires O_RDONLY access mode",
144 					  DICT_TYPE_RANDOM, name));
145 
146     /*
147      * Bundle up preliminary results.
148      */
149     dict_random =
150 	(DICT_RANDOM *) dict_alloc(DICT_TYPE_RANDOM, name, sizeof(*dict_random));
151     dict_random->dict.lookup = dict_random_lookup;
152     dict_random->dict.close = dict_random_close;
153     dict_random->dict.flags = dict_flags | DICT_FLAG_PATTERN;
154     dict_random->replies = 0;
155     dict_random->dict.owner.status = DICT_OWNER_TRUSTED;
156     dict_random->dict.owner.uid = 0;
157 
158     /*
159      * Split the table name into its constituent parts.
160      */
161     if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0
162 	|| *(saved_name = mystrndup(name + 1, len - 2)) == 0
163 	|| (err = dict_random_parse_name(&dict_random->dict,
164 					 &dict_random->replies, saved_name,
165 					 CHARS_COMMA_SP, CHARS_BRACE)) != 0
166 	|| dict_random->replies->argc == 0) {
167 	dict_random_close(&dict_random->dict);
168 	DICT_RANDOM_RETURN(err == 0 ?
169 			   dict_surrogate(DICT_TYPE_RANDOM, name,
170 					  open_flags, dict_flags,
171 					  "bad syntax: \"%s:%s\"; "
172 					  "need \"%s:{value...}\"",
173 					  DICT_TYPE_RANDOM, name,
174 					  DICT_TYPE_RANDOM) :
175 			   dict_surrogate(DICT_TYPE_RANDOM, name,
176 					  open_flags, dict_flags,
177 					  "%s", err));
178     }
179     dict_file_purge_buffers(&dict_random->dict);
180     DICT_RANDOM_RETURN(DICT_DEBUG (&dict_random->dict));
181 }
182