Postfix3.3.1
dict_thash.c
[詳解]
1 /*++
2 /* NAME
3 /* dict_thash 3
4 /* SUMMARY
5 /* dictionary manager interface to hashed flat text files
6 /* SYNOPSIS
7 /* #include <dict_thash.h>
8 /*
9 /* DICT *dict_thash_open(path, open_flags, dict_flags)
10 /* const char *name;
11 /* const char *path;
12 /* int open_flags;
13 /* int dict_flags;
14 /* DESCRIPTION
15 /* dict_thash_open() opens the named flat text file, creates
16 /* an in-memory hash table, and makes it available via the
17 /* generic interface described in dict_open(3). The input
18 /* format is as with postmap(1).
19 /* DIAGNOSTICS
20 /* Fatal errors: cannot open file, out of memory.
21 /* SEE ALSO
22 /* dict(3) generic dictionary manager
23 /* LICENSE
24 /* .ad
25 /* .fi
26 /* The Secure Mailer license must be distributed with this software.
27 /* AUTHOR(S)
28 /* Wietse Venema
29 /* IBM T.J. Watson Research
30 /* P.O. Box 704
31 /* Yorktown Heights, NY 10598, USA
32 /*
33 /* Wietse Venema
34 /* Google, Inc.
35 /* 111 8th Avenue
36 /* New York, NY 10011, USA
37 /*--*/
38 
39 /* System library. */
40 
41 #include <sys_defs.h>
42 #include <sys/stat.h>
43 #include <ctype.h>
44 #include <string.h>
45 
46 /* Utility library. */
47 
48 #include <msg.h>
49 #include <iostuff.h>
50 #include <vstring.h>
51 #include <stringops.h>
52 #include <readlline.h>
53 #include <dict.h>
54 #include <dict_ht.h>
55 #include <dict_thash.h>
56 
57 /* Application-specific. */
58 
59 #define STR vstring_str
60 #define LEN VSTRING_LEN
61 
62 /* dict_thash_open - open flat text data base */
63 
64 DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
65 {
66  DICT *dict;
67  VSTREAM *fp = 0; /* DICT_THASH_OPEN_RETURN() */
68  struct stat st;
69  time_t before;
70  time_t after;
71  VSTRING *line_buffer = 0; /* DICT_THASH_OPEN_RETURN() */
72  int lineno;
73  int last_line;
74  char *key;
75  char *value;
76 
77  /*
78  * Let the optimizer worry about eliminating redundant code.
79  */
80 #define DICT_THASH_OPEN_RETURN(d) do { \
81  DICT *__d = (d); \
82  if (fp != 0) \
83  vstream_fclose(fp); \
84  if (line_buffer != 0) \
85  vstring_free(line_buffer); \
86  return (__d); \
87  } while (0)
88 
89  /*
90  * Sanity checks.
91  */
92  if (open_flags != O_RDONLY)
94  open_flags, dict_flags,
95  "%s:%s map requires O_RDONLY access mode",
96  DICT_TYPE_THASH, path));
97 
98  /*
99  * Read the flat text file into in-memory hash. Read the file again if it
100  * may have changed while we were reading.
101  */
102  for (before = time((time_t *) 0); /* see below */ ; before = after) {
103  if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) {
105  open_flags, dict_flags,
106  "open database %s: %m", path));
107  }
108 
109  /*
110  * Reuse the "internal" dictionary type.
111  */
112  dict = dict_open3(DICT_TYPE_HT, path, open_flags, dict_flags);
114 
115  /*
116  * XXX This duplicates the parser in postmap.c.
117  */
118  if (line_buffer == 0)
119  line_buffer = vstring_alloc(100);
120  last_line = 0;
121  while (readllines(line_buffer, fp, &last_line, &lineno)) {
122  int in_quotes = 0;
123 
124  /*
125  * First some UTF-8 checks sans casefolding.
126  */
127  if ((dict->flags & DICT_FLAG_UTF8_ACTIVE)
128  && allascii(STR(line_buffer)) == 0
129  && valid_utf8_string(STR(line_buffer), LEN(line_buffer)) == 0) {
130  msg_warn("%s, line %d: non-UTF-8 input \"%s\""
131  " -- ignoring this line",
132  VSTREAM_PATH(fp), lineno, STR(line_buffer));
133  continue;
134  }
135 
136  /*
137  * Split on the first whitespace character, then trim leading and
138  * trailing whitespace from key and value.
139  */
140  for (value = STR(line_buffer); *value; value++) {
141  if (*value == '\\') {
142  if (*++value == 0)
143  break;
144  } else if (ISSPACE(*value)) {
145  if (!in_quotes)
146  break;
147  } else if (*value == '"') {
148  in_quotes = !in_quotes;
149  }
150  }
151  if (in_quotes) {
152  msg_warn("%s, line %d: unbalanced '\"' in '%s'"
153  " -- ignoring this line",
154  VSTREAM_PATH(fp), lineno, STR(line_buffer));
155  continue;
156  }
157  if (*value)
158  *value++ = 0;
159  while (ISSPACE(*value))
160  value++;
161  trimblanks(value, 0)[0] = 0;
162 
163  /*
164  * Leave the key in quoted form, for consistency with postmap.c
165  * and dict_inline.c.
166  */
167  key = STR(line_buffer);
168 
169  /*
170  * Enforce the "key whitespace value" format. Disallow missing
171  * keys or missing values.
172  */
173  if (*key == 0 || *value == 0) {
174  msg_warn("%s, line %d: expected format: key whitespace value"
175  " -- ignoring this line", path, lineno);
176  continue;
177  }
178  if (key[strlen(key) - 1] == ':')
179  msg_warn("%s, line %d: record is in \"key: value\" format;"
180  " is this an alias file?", path, lineno);
181 
182  /*
183  * Store the value under the key. Handle duplicates
184  * appropriately. XXX Move this into dict_ht, but 1) that map
185  * ignores duplicates by default and we would have to check that
186  * we won't break existing code that depends on such benavior; 2)
187  * by inlining the checks here we can degrade gracefully instead
188  * of terminating with a fatal error. See comment in
189  * dict_inline.c.
190  */
191  if (dict->lookup(dict, key) != 0) {
192  if (dict_flags & DICT_FLAG_DUP_IGNORE) {
193  /* void */ ;
194  } else if (dict_flags & DICT_FLAG_DUP_REPLACE) {
195  dict->update(dict, key, value);
196  } else if (dict_flags & DICT_FLAG_DUP_WARN) {
197  msg_warn("%s, line %d: duplicate entry: \"%s\"",
198  path, lineno, key);
199  } else {
200  dict->close(dict);
202  open_flags, dict_flags,
203  "%s, line %d: duplicate entry: \"%s\"",
204  path, lineno, key));
205  }
206  } else {
207  dict->update(dict, key, value);
208  }
209  }
210 
211  /*
212  * See if the source file is hot.
213  */
214  if (fstat(vstream_fileno(fp), &st) < 0)
215  msg_fatal("fstat %s: %m", path);
216  if (vstream_fclose(fp))
217  msg_fatal("read %s: %m", path);
218  fp = 0; /* DICT_THASH_OPEN_RETURN() */
219  after = time((time_t *) 0);
220  if (st.st_mtime < before - 1 || st.st_mtime > after)
221  break;
222 
223  /*
224  * Yes, it is hot. Discard the result and read the file again.
225  */
226  dict->close(dict);
227  if (msg_verbose > 1)
228  msg_info("pausing to let file %s cool down", path);
229  doze(300000);
230  }
231 
232  dict->owner.uid = st.st_uid;
233  dict->owner.status = (st.st_uid != 0);
234 
236 }
int msg_verbose
Definition: msg.c:177
#define DICT_FLAG_DUP_IGNORE
Definition: dict.h:111
uid_t uid
Definition: dict.h:39
#define STR
Definition: dict_thash.c:59
#define DICT_TYPE_HT
Definition: dict_ht.h:23
void(* close)(struct DICT *)
Definition: dict.h:87
#define stat(p, s)
Definition: warn_stat.h:18
int flags
Definition: dict.h:81
int valid_utf8_string(const char *, ssize_t)
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
Definition: vstream.c:1241
#define DICT_THASH_OPEN_RETURN(d)
DICT * dict_open3(const char *, const char *, int, int)
Definition: dict_open.c:439
Definition: dict.h:78
int(* update)(struct DICT *, const char *, const char *)
Definition: dict.h:83
#define DICT_FLAG_DUP_REPLACE
Definition: dict.h:117
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
#define LEN
Definition: dict_thash.c:60
void dict_type_override(DICT *, const char *)
Definition: dict_open.c:563
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
DICT * dict_thash_open(const char *path, int open_flags, int dict_flags)
Definition: dict_thash.c:64
void doze(unsigned delay)
Definition: doze.c:44
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:82
#define allascii(s)
Definition: stringops.h:66
#define DICT_FLAG_DUP_WARN
Definition: dict.h:110
char * trimblanks(char *, ssize_t)
Definition: trimblanks.c:37
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
int status
Definition: dict.h:38
VSTRING * readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
Definition: readlline.c:82
#define vstream_fileno(vp)
Definition: vstream.h:115
#define ISSPACE(c)
Definition: sys_defs.h:1753
DICT_OWNER owner
Definition: dict.h:93
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
#define DICT_FLAG_UTF8_ACTIVE
Definition: dict.h:131
#define fstat(f, s)
Definition: warn_stat.h:20
void msg_info(const char *fmt,...)
Definition: msg.c:199
#define DICT_TYPE_THASH
Definition: dict_thash.h:22