xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict_thash.c (revision 80d9064ac03cbb6a4174695f0d5b237c8766d3d0)
1 /*	$NetBSD: dict_thash.c,v 1.1.1.3 2014/07/06 19:27:58 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dict_thash 3
6 /* SUMMARY
7 /*	dictionary manager interface to hashed flat text files
8 /* SYNOPSIS
9 /*	#include <dict_thash.h>
10 /*
11 /*	DICT	*dict_thash_open(path, open_flags, dict_flags)
12 /*	const char *name;
13 /*	const char *path;
14 /*	int	open_flags;
15 /*	int	dict_flags;
16 /* DESCRIPTION
17 /*	dict_thash_open() opens the named flat text file, creates
18 /*	an in-memory hash table, and makes it available via the
19 /*	generic interface described in dict_open(3). The input
20 /*	format is as with postmap(1).
21 /* DIAGNOSTICS
22 /*	Fatal errors: cannot open file, out of memory.
23 /* SEE ALSO
24 /*	dict(3) generic dictionary manager
25 /* LICENSE
26 /* .ad
27 /* .fi
28 /*	The Secure Mailer license must be distributed with this software.
29 /* AUTHOR(S)
30 /*	Wietse Venema
31 /*	IBM T.J. Watson Research
32 /*	P.O. Box 704
33 /*	Yorktown Heights, NY 10598, USA
34 /*--*/
35 
36 /* System library. */
37 
38 #include <sys_defs.h>
39 #include <sys/stat.h>
40 #include <ctype.h>
41 #include <string.h>
42 
43 /* Utility library. */
44 
45 #include <msg.h>
46 #include <mymalloc.h>
47 #include <htable.h>
48 #include <iostuff.h>
49 #include <vstring.h>
50 #include <stringops.h>
51 #include <readlline.h>
52 #include <dict.h>
53 #include <dict_thash.h>
54 #include <warn_stat.h>
55 
56 /* Application-specific. */
57 
58 typedef struct {
59     DICT    dict;			/* generic members */
60     HTABLE *table;			/* in-memory hash */
61     HTABLE_INFO **info;			/* for iterator */
62     HTABLE_INFO **cursor;		/* ditto */
63 } DICT_THASH;
64 
65 #define STR	vstring_str
66 
67 /* dict_thash_lookup - find database entry */
68 
69 static const char *dict_thash_lookup(DICT *dict, const char *name)
70 {
71     DICT_THASH *dict_thash = (DICT_THASH *) dict;
72     const char *result = 0;
73 
74     /*
75      * Optionally fold the key.
76      */
77     if (dict->flags & DICT_FLAG_FOLD_FIX) {
78 	if (dict->fold_buf == 0)
79 	    dict->fold_buf = vstring_alloc(10);
80 	vstring_strcpy(dict->fold_buf, name);
81 	name = lowercase(vstring_str(dict->fold_buf));
82     }
83 
84     /*
85      * Look up the value.
86      */
87     result = htable_find(dict_thash->table, name);
88 
89     DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, result);
90 }
91 
92 /* dict_thash_sequence - traverse the dictionary */
93 
94 static int dict_thash_sequence(DICT *dict, int function,
95 			               const char **key, const char **value)
96 {
97     const char *myname = "dict_thash_sequence";
98     DICT_THASH *dict_thash = (DICT_THASH *) dict;
99 
100     /*
101      * Determine and execute the seek function.
102      */
103     switch (function) {
104     case DICT_SEQ_FUN_FIRST:
105 	if (dict_thash->info == 0)
106 	    dict_thash->info = htable_list(dict_thash->table);
107 	dict_thash->cursor = dict_thash->info;
108 	break;
109     case DICT_SEQ_FUN_NEXT:
110 	if (dict_thash->cursor[0])
111 	    dict_thash->cursor += 1;
112 	break;
113     default:
114 	msg_panic("%s: invalid function: %d", myname, function);
115     }
116 
117     /*
118      * Return the entry under the cursor.
119      */
120     if (dict_thash->cursor[0]) {
121 	*key = dict_thash->cursor[0]->key;
122 	*value = dict_thash->cursor[0]->value;
123 	DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
124     } else {
125 	*key = 0;
126 	*value = 0;
127 	DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
128     }
129 }
130 
131 /* dict_thash_close - disassociate from data base */
132 
133 static void dict_thash_close(DICT *dict)
134 {
135     DICT_THASH *dict_thash = (DICT_THASH *) dict;
136 
137     htable_free(dict_thash->table, myfree);
138     if (dict_thash->info)
139 	myfree((char *) dict_thash->info);
140     if (dict->fold_buf)
141 	vstring_free(dict->fold_buf);
142     dict_free(dict);
143 }
144 
145 /* dict_thash_open - open flat text data base */
146 
147 DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
148 {
149     DICT_THASH *dict_thash;
150     VSTREAM *fp = 0;
151     struct stat st;
152     time_t  before;
153     time_t  after;
154     VSTRING *line_buffer = 0;
155     int     lineno;
156     char   *key;
157     char   *value;
158     HTABLE *table;
159     HTABLE_INFO *ht;
160 
161     /*
162      * Let the optimizer worry about eliminating redundant code.
163      */
164 #define DICT_THASH_OPEN_RETURN(d) { \
165 	DICT *__d = (d); \
166 	if (fp != 0) \
167 	    vstream_fclose(fp); \
168 	if (line_buffer != 0) \
169 	    vstring_free(line_buffer); \
170 	return (__d); \
171     } while (0)
172 
173     /*
174      * Sanity checks.
175      */
176     if (open_flags != O_RDONLY)
177 	DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
178 					      open_flags, dict_flags,
179 				  "%s:%s map requires O_RDONLY access mode",
180 					      DICT_TYPE_THASH, path));
181 
182     /*
183      * Read the flat text file into in-memory hash. Read the file again if it
184      * may have changed while we were reading.
185      */
186     for (before = time((time_t *) 0); /* see below */ ; before = after) {
187 	if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) {
188 	    DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
189 						  open_flags, dict_flags,
190 					     "open database %s: %m", path));
191 	}
192 	if (line_buffer == 0)
193 	    line_buffer = vstring_alloc(100);
194 	lineno = 0;
195 	table = htable_create(13);
196 	while (readlline(line_buffer, fp, &lineno)) {
197 
198 	    /*
199 	     * Split on the first whitespace character, then trim leading and
200 	     * trailing whitespace from key and value.
201 	     */
202 	    key = STR(line_buffer);
203 	    value = key + strcspn(key, " \t\r\n");
204 	    if (*value)
205 		*value++ = 0;
206 	    while (ISSPACE(*value))
207 		value++;
208 	    trimblanks(key, 0)[0] = 0;
209 	    trimblanks(value, 0)[0] = 0;
210 
211 	    /*
212 	     * Enforce the "key whitespace value" format. Disallow missing
213 	     * keys or missing values.
214 	     */
215 	    if (*key == 0 || *value == 0) {
216 		msg_warn("%s, line %d: expected format: key whitespace value"
217 			 " -- ignoring this line", path, lineno);
218 		continue;
219 	    }
220 	    if (key[strlen(key) - 1] == ':')
221 		msg_warn("%s, line %d: record is in \"key: value\" format;"
222 			 " is this an alias file?", path, lineno);
223 
224 	    /*
225 	     * Optionally fold the key.
226 	     */
227 	    if (dict_flags & DICT_FLAG_FOLD_FIX)
228 		lowercase(key);
229 
230 	    /*
231 	     * Store the value under the key. Handle duplicates
232 	     * appropriately.
233 	     */
234 	    if ((ht = htable_locate(table, key)) != 0) {
235 		if (dict_flags & DICT_FLAG_DUP_IGNORE) {
236 		     /* void */ ;
237 		} else if (dict_flags & DICT_FLAG_DUP_REPLACE) {
238 		    myfree(ht->value);
239 		    ht->value = mystrdup(value);
240 		} else if (dict_flags & DICT_FLAG_DUP_WARN) {
241 		    msg_warn("%s, line %d: duplicate entry: \"%s\"",
242 			     path, lineno, key);
243 		} else {
244 		    msg_fatal("%s, line %d: duplicate entry: \"%s\"",
245 			      path, lineno, key);
246 		}
247 	    } else {
248 		htable_enter(table, key, mystrdup(value));
249 	    }
250 	}
251 
252 	/*
253 	 * See if the source file is hot.
254 	 */
255 	if (fstat(vstream_fileno(fp), &st) < 0)
256 	    msg_fatal("fstat %s: %m", path);
257 	if (vstream_fclose(fp))
258 	    msg_fatal("read %s: %m", path);
259 	fp = 0;					/* DICT_THASH_OPEN_RETURN() */
260 	after = time((time_t *) 0);
261 	if (st.st_mtime < before - 1 || st.st_mtime > after)
262 	    break;
263 
264 	/*
265 	 * Yes, it is hot. Discard the result and read the file again.
266 	 */
267 	htable_free(table, myfree);
268 	if (msg_verbose > 1)
269 	    msg_info("pausing to let file %s cool down", path);
270 	doze(300000);
271     }
272 
273     /*
274      * Create the in-memory table.
275      */
276     dict_thash = (DICT_THASH *)
277 	dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
278     dict_thash->dict.lookup = dict_thash_lookup;
279     dict_thash->dict.sequence = dict_thash_sequence;
280     dict_thash->dict.close = dict_thash_close;
281     dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
282     if (dict_flags & DICT_FLAG_FOLD_FIX)
283 	dict_thash->dict.fold_buf = vstring_alloc(10);
284     dict_thash->info = 0;
285     dict_thash->table = table;
286     dict_thash->dict.owner.uid = st.st_uid;
287     dict_thash->dict.owner.status = (st.st_uid != 0);
288 
289     DICT_THASH_OPEN_RETURN(DICT_DEBUG (&dict_thash->dict));
290 }
291