No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / attr_scan0.c
blob81398dd66403648b1ce6c394661e701de8ebfa2a
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* attr_scan0 3
6 /* SUMMARY
7 /* recover attributes from byte stream
8 /* SYNOPSIS
9 /* #include <attr.h>
11 /* int attr_scan0(fp, flags, type, name, ..., ATTR_TYPE_END)
12 /* VSTREAM fp;
13 /* int flags;
14 /* int type;
15 /* char *name;
17 /* int attr_vscan0(fp, flags, ap)
18 /* VSTREAM fp;
19 /* int flags;
20 /* va_list ap;
21 /* DESCRIPTION
22 /* attr_scan0() takes zero or more (name, value) request attributes
23 /* and recovers the attribute values from the byte stream that was
24 /* possibly generated by attr_print0().
26 /* attr_vscan0() provides an alternative interface that is convenient
27 /* for calling from within a variadic function.
29 /* The input stream is formatted as follows, where (item)* stands
30 /* for zero or more instances of the specified item, and where
31 /* (item1 | item2) stands for choice:
33 /* .in +5
34 /* attr-list :== simple-attr* null
35 /* .br
36 /* simple-attr :== attr-name null attr-value null
37 /* .br
38 /* attr-name :== any string not containing null
39 /* .br
40 /* attr-value :== any string not containing null
41 /* .br
42 /* null :== the ASCII null character
43 /* .in
45 /* All attribute names and attribute values are sent as null terminated
46 /* strings. Each string must be no longer than 4*var_line_limit
47 /* characters including the terminator.
48 /* These formatting rules favor implementations in C.
50 /* Normally, attributes must be received in the sequence as specified with
51 /* the attr_scan0() argument list. The input stream may contain additional
52 /* attributes at any point in the input stream, including additional
53 /* instances of requested attributes.
55 /* Additional input attributes or input attribute instances are silently
56 /* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
57 /* (see below). This allows for some flexibility in the evolution of
58 /* protocols while still providing the option of being strict where
59 /* this is desirable.
61 /* Arguments:
62 /* .IP fp
63 /* Stream to recover the input attributes from.
64 /* .IP flags
65 /* The bit-wise OR of zero or more of the following.
66 /* .RS
67 /* .IP ATTR_FLAG_MISSING
68 /* Log a warning when the input attribute list terminates before all
69 /* requested attributes are recovered. It is always an error when the
70 /* input stream ends without the newline attribute list terminator.
71 /* .IP ATTR_FLAG_EXTRA
72 /* Log a warning and stop attribute recovery when the input stream
73 /* contains an attribute that was not requested. This includes the
74 /* case of additional instances of a requested attribute.
75 /* .IP ATTR_FLAG_MORE
76 /* After recovering the requested attributes, leave the input stream
77 /* in a state that is usable for more attr_scan0() operations from the
78 /* same input attribute list.
79 /* By default, attr_scan0() skips forward past the input attribute list
80 /* terminator.
81 /* .IP ATTR_FLAG_STRICT
82 /* For convenience, this value combines both ATTR_FLAG_MISSING and
83 /* ATTR_FLAG_EXTRA.
84 /* .IP ATTR_FLAG_NONE
85 /* For convenience, this value requests none of the above.
86 /* .RE
87 /* .IP type
88 /* The type argument determines the arguments that follow.
89 /* .RS
90 /* .IP "ATTR_TYPE_INT (char *, int *)"
91 /* This argument is followed by an attribute name and an integer pointer.
92 /* .IP "ATTR_TYPE_LONG (char *, long *)"
93 /* This argument is followed by an attribute name and a long pointer.
94 /* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
95 /* This argument is followed by an attribute name and a VSTRING pointer.
96 /* .IP "ATTR_TYPE_DATA (char *, VSTRING *)"
97 /* This argument is followed by an attribute name and a VSTRING pointer.
98 /* .IP "ATTR_TYPE_FUNC (ATTR_SCAN_SLAVE_FN, void *)"
99 /* This argument is followed by a function pointer and a generic data
100 /* pointer. The caller-specified function returns < 0 in case of
101 /* error.
102 /* .IP "ATTR_TYPE_HASH (HTABLE *)"
103 /* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
104 /* All further input attributes are processed as string attributes.
105 /* No specific attribute sequence is enforced.
106 /* All attributes up to the attribute list terminator are read,
107 /* but only the first instance of each attribute is stored.
108 /* There can be no more than 1024 attributes in a hash table.
109 /* .sp
110 /* The attribute string values are stored in the hash table under
111 /* keys equal to the attribute name (obtained from the input stream).
112 /* Values from the input stream are added to the hash table. Existing
113 /* hash table entries are not replaced.
114 /* .sp
115 /* N.B. This construct must be followed by an ATTR_TYPE_END argument.
116 /* .IP ATTR_TYPE_END
117 /* This argument terminates the requested attribute list.
118 /* .RE
119 /* BUGS
120 /* ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary
121 /* names from possibly untrusted sources.
122 /* This is unsafe, unless the resulting table is queried only with
123 /* known to be good attribute names.
124 /* DIAGNOSTICS
125 /* attr_scan0() and attr_vscan0() return -1 when malformed input is
126 /* detected (string too long, incomplete line, missing end marker).
127 /* Otherwise, the result value is the number of attributes that were
128 /* successfully recovered from the input stream (a hash table counts
129 /* as the number of entries stored into the table).
131 /* Panic: interface violation. All system call errors are fatal.
132 /* SEE ALSO
133 /* attr_print0(3) send attributes over byte stream.
134 /* LICENSE
135 /* .ad
136 /* .fi
137 /* The Secure Mailer license must be distributed with this software.
138 /* AUTHOR(S)
139 /* Wietse Venema
140 /* IBM T.J. Watson Research
141 /* P.O. Box 704
142 /* Yorktown Heights, NY 10598, USA
143 /*--*/
145 /* System library. */
147 #include <sys_defs.h>
148 #include <stdarg.h>
149 #include <string.h>
150 #include <stdio.h>
152 /* Utility library. */
154 #include <msg.h>
155 #include <mymalloc.h>
156 #include <vstream.h>
157 #include <vstring.h>
158 #include <vstring_vstream.h>
159 #include <htable.h>
160 #include <base64_code.h>
161 #include <attr.h>
163 /* Application specific. */
165 #define STR(x) vstring_str(x)
166 #define LEN(x) VSTRING_LEN(x)
168 /* attr_scan0_string - pull a string from the input stream */
170 static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
172 int ch;
174 if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) {
175 msg_warn("%s on %s while reading %s",
176 vstream_ftimeout(fp) ? "timeout" : "premature end-of-input",
177 VSTREAM_PATH(fp), context);
178 return (-1);
180 if (ch != 0) {
181 msg_warn("unexpected end-of-input from %s while reading %s",
182 VSTREAM_PATH(fp), context);
183 return (-1);
185 if (msg_verbose)
186 msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
187 return (ch);
190 /* attr_scan0_data - pull a data blob from the input stream */
192 static int attr_scan0_data(VSTREAM *fp, VSTRING *str_buf,
193 const char *context)
195 static VSTRING *base64_buf = 0;
196 int ch;
198 if (base64_buf == 0)
199 base64_buf = vstring_alloc(10);
200 if ((ch = attr_scan0_string(fp, base64_buf, context)) < 0)
201 return (-1);
202 if (base64_decode(str_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
203 msg_warn("malformed base64 data from %s while reading %s: %.100s",
204 VSTREAM_PATH(fp), context, STR(base64_buf));
205 return (-1);
207 return (ch);
210 /* attr_scan0_number - pull a number from the input stream */
212 static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
213 const char *context)
215 char junk = 0;
216 int ch;
218 if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
219 return (-1);
220 if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
221 msg_warn("malformed numerical data from %s while reading %s: %.100s",
222 VSTREAM_PATH(fp), context, STR(str_buf));
223 return (-1);
225 return (ch);
228 /* attr_scan0_long_number - pull a number from the input stream */
230 static int attr_scan0_long_number(VSTREAM *fp, unsigned long *ptr,
231 VSTRING *str_buf,
232 const char *context)
234 char junk = 0;
235 int ch;
237 if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
238 return (-1);
239 if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
240 msg_warn("malformed numerical data from %s while reading %s: %.100s",
241 VSTREAM_PATH(fp), context, STR(str_buf));
242 return (-1);
244 return (ch);
247 /* attr_vscan0 - receive attribute list from stream */
249 int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
251 const char *myname = "attr_scan0";
252 static VSTRING *str_buf = 0;
253 static VSTRING *name_buf = 0;
254 int wanted_type = -1;
255 char *wanted_name;
256 unsigned int *number;
257 unsigned long *long_number;
258 VSTRING *string;
259 HTABLE *hash_table;
260 int ch;
261 int conversions;
262 ATTR_SCAN_SLAVE_FN scan_fn;
263 void *scan_arg;
266 * Sanity check.
268 if (flags & ~ATTR_FLAG_ALL)
269 msg_panic("%s: bad flags: 0x%x", myname, flags);
272 * EOF check.
274 if ((ch = VSTREAM_GETC(fp)) == VSTREAM_EOF)
275 return (0);
276 vstream_ungetc(fp, ch);
279 * Initialize.
281 if (str_buf == 0) {
282 str_buf = vstring_alloc(10);
283 name_buf = vstring_alloc(10);
287 * Iterate over all (type, name, value) triples.
289 for (conversions = 0; /* void */ ; conversions++) {
292 * Determine the next attribute type and attribute name on the
293 * caller's wish list.
295 * If we're reading into a hash table, we already know that the
296 * attribute value is string-valued, and we get the attribute name
297 * from the input stream instead. This is secure only when the
298 * resulting table is queried with known to be good attribute names.
300 if (wanted_type != ATTR_TYPE_HASH) {
301 wanted_type = va_arg(ap, int);
302 if (wanted_type == ATTR_TYPE_END) {
303 if ((flags & ATTR_FLAG_MORE) != 0)
304 return (conversions);
305 wanted_name = "(list terminator)";
306 } else if (wanted_type == ATTR_TYPE_HASH) {
307 wanted_name = "(any attribute name or list terminator)";
308 hash_table = va_arg(ap, HTABLE *);
309 if (va_arg(ap, int) != ATTR_TYPE_END)
310 msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
311 myname);
312 } else if (wanted_type != ATTR_TYPE_FUNC) {
313 wanted_name = va_arg(ap, char *);
318 * Locate the next attribute of interest in the input stream.
320 while (wanted_type != ATTR_TYPE_FUNC) {
323 * Get the name of the next attribute. Hitting EOF is always bad.
324 * Hitting the end-of-input early is OK if the caller is prepared
325 * to deal with missing inputs.
327 if (msg_verbose)
328 msg_info("%s: wanted attribute: %s",
329 VSTREAM_PATH(fp), wanted_name);
330 if ((ch = attr_scan0_string(fp, name_buf,
331 "input attribute name")) == VSTREAM_EOF)
332 return (-1);
333 if (LEN(name_buf) == 0) {
334 if (wanted_type == ATTR_TYPE_END
335 || wanted_type == ATTR_TYPE_HASH)
336 return (conversions);
337 if ((flags & ATTR_FLAG_MISSING) != 0)
338 msg_warn("missing attribute %s in input from %s",
339 wanted_name, VSTREAM_PATH(fp));
340 return (conversions);
344 * See if the caller asks for this attribute.
346 if (wanted_type == ATTR_TYPE_HASH
347 || (wanted_type != ATTR_TYPE_END
348 && strcmp(wanted_name, STR(name_buf)) == 0))
349 break;
350 if ((flags & ATTR_FLAG_EXTRA) != 0) {
351 msg_warn("unexpected attribute %s from %s (expecting: %s)",
352 STR(name_buf), VSTREAM_PATH(fp), wanted_name);
353 return (conversions);
357 * Skip over this attribute. The caller does not ask for it.
359 (void) attr_scan0_string(fp, str_buf, "input attribute value");
363 * Do the requested conversion.
365 switch (wanted_type) {
366 case ATTR_TYPE_INT:
367 number = va_arg(ap, unsigned int *);
368 if ((ch = attr_scan0_number(fp, number, str_buf,
369 "input attribute value")) < 0)
370 return (-1);
371 break;
372 case ATTR_TYPE_LONG:
373 long_number = va_arg(ap, unsigned long *);
374 if ((ch = attr_scan0_long_number(fp, long_number, str_buf,
375 "input attribute value")) < 0)
376 return (-1);
377 break;
378 case ATTR_TYPE_STR:
379 string = va_arg(ap, VSTRING *);
380 if ((ch = attr_scan0_string(fp, string,
381 "input attribute value")) < 0)
382 return (-1);
383 break;
384 case ATTR_TYPE_DATA:
385 string = va_arg(ap, VSTRING *);
386 if ((ch = attr_scan0_data(fp, string,
387 "input attribute value")) < 0)
388 return (-1);
389 break;
390 case ATTR_TYPE_FUNC:
391 scan_fn = va_arg(ap, ATTR_SCAN_SLAVE_FN);
392 scan_arg = va_arg(ap, void *);
393 if (scan_fn(attr_scan0, fp, flags | ATTR_FLAG_MORE, scan_arg) < 0)
394 return (-1);
395 break;
396 case ATTR_TYPE_HASH:
397 if ((ch = attr_scan0_string(fp, str_buf,
398 "input attribute value")) < 0)
399 return (-1);
400 if (htable_locate(hash_table, STR(name_buf)) != 0) {
401 if ((flags & ATTR_FLAG_EXTRA) != 0) {
402 msg_warn("duplicate attribute %s in input from %s",
403 STR(name_buf), VSTREAM_PATH(fp));
404 return (conversions);
406 } else if (hash_table->used >= ATTR_HASH_LIMIT) {
407 msg_warn("attribute count exceeds limit %d in input from %s",
408 ATTR_HASH_LIMIT, VSTREAM_PATH(fp));
409 return (conversions);
410 } else {
411 htable_enter(hash_table, STR(name_buf),
412 mystrdup(STR(str_buf)));
414 break;
415 default:
416 msg_panic("%s: unknown type code: %d", myname, wanted_type);
421 /* attr_scan0 - read attribute list from stream */
423 int attr_scan0(VSTREAM *fp, int flags,...)
425 va_list ap;
426 int ret;
428 va_start(ap, flags);
429 ret = attr_vscan0(fp, flags, ap);
430 va_end(ap);
431 return (ret);
434 #ifdef TEST
437 * Proof of concept test program. Mirror image of the attr_scan0 test
438 * program.
440 #include <msg_vstream.h>
442 int var_line_limit = 2048;
444 int main(int unused_argc, char **used_argv)
446 VSTRING *data_val = vstring_alloc(1);
447 VSTRING *str_val = vstring_alloc(1);
448 HTABLE *table = htable_create(1);
449 HTABLE_INFO **ht_info_list;
450 HTABLE_INFO **ht;
451 int int_val;
452 long long_val;
453 int ret;
455 msg_verbose = 1;
456 msg_vstream_init(used_argv[0], VSTREAM_ERR);
457 if ((ret = attr_scan0(VSTREAM_IN,
458 ATTR_FLAG_STRICT,
459 ATTR_TYPE_INT, ATTR_NAME_INT, &int_val,
460 ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
461 ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
462 ATTR_TYPE_DATA, ATTR_NAME_DATA, data_val,
463 ATTR_TYPE_HASH, table,
464 ATTR_TYPE_END)) > 4) {
465 vstream_printf("%s %d\n", ATTR_NAME_INT, int_val);
466 vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
467 vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
468 vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(str_val));
469 ht_info_list = htable_list(table);
470 for (ht = ht_info_list; *ht; ht++)
471 vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
472 myfree((char *) ht_info_list);
473 } else {
474 vstream_printf("return: %d\n", ret);
476 if ((ret = attr_scan0(VSTREAM_IN,
477 ATTR_FLAG_STRICT,
478 ATTR_TYPE_INT, ATTR_NAME_INT, &int_val,
479 ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
480 ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
481 ATTR_TYPE_DATA, ATTR_NAME_DATA, data_val,
482 ATTR_TYPE_END)) == 4) {
483 vstream_printf("%s %d\n", ATTR_NAME_INT, int_val);
484 vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
485 vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
486 vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(data_val));
487 ht_info_list = htable_list(table);
488 for (ht = ht_info_list; *ht; ht++)
489 vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
490 myfree((char *) ht_info_list);
491 } else {
492 vstream_printf("return: %d\n", ret);
494 if (vstream_fflush(VSTREAM_OUT) != 0)
495 msg_fatal("write error: %m");
497 vstring_free(data_val);
498 vstring_free(str_val);
499 htable_free(table, myfree);
501 return (0);
504 #endif